Compare commits
	
		
			77 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f591433118 | ||
| 
						 | 
					5a897a34c2 | ||
| 
						 | 
					138423de4a | ||
| 
						 | 
					1d9f9c4487 | ||
| 
						 | 
					82f71a6c91 | ||
| 
						 | 
					0c85299efc | ||
| 
						 | 
					50195aae1d | ||
| 
						 | 
					586425bc2d | ||
| 
						 | 
					b0bde6b652 | ||
| 
						 | 
					be2369c64c | ||
| 
						 | 
					c2cf68cab6 | ||
| 
						 | 
					f7d1863615 | ||
| 
						 | 
					0bb0aee6cb | ||
| 
						 | 
					b392e019f3 | ||
| 
						 | 
					8eb5fa7394 | ||
| 
						 | 
					c5c04db81a | ||
| 
						 | 
					4056cfaa15 | ||
| 
						 | 
					ba8b226d89 | ||
| 
						 | 
					88df2edb95 | ||
| 
						 | 
					b856cf2141 | ||
| 
						 | 
					f4a2429a13 | ||
| 
						 | 
					7d1f8ad4e7 | ||
| 
						 | 
					82656db49d | ||
| 
						 | 
					d6dac912dd | ||
| 
						 | 
					207f9398b4 | ||
| 
						 | 
					7b51a82de8 | ||
| 
						 | 
					66f08bf0db | ||
| 
						 | 
					90384e921f | ||
| 
						 | 
					1ae7c2697c | ||
| 
						 | 
					536143c1d7 | ||
| 
						 | 
					6025950aab | ||
| 
						 | 
					4d69940b96 | ||
| 
						 | 
					b1f49cae13 | ||
| 
						 | 
					80e9be5343 | ||
| 
						 | 
					d6d6b76fa5 | ||
| 
						 | 
					d18a352a82 | ||
| 
						 | 
					5fbaabb47e | ||
| 
						 | 
					83cc13b817 | ||
| 
						 | 
					1679cda4c5 | ||
| 
						 | 
					e5f11aafc1 | ||
| 
						 | 
					f262c77b17 | ||
| 
						 | 
					3ef00effc5 | ||
| 
						 | 
					1ce13bea91 | ||
| 
						 | 
					eaab366dcb | ||
| 
						 | 
					784f06c415 | ||
| 
						 | 
					4b07e80a8f | ||
| 
						 | 
					9f7fac407d | ||
| 
						 | 
					47b6668876 | ||
| 
						 | 
					951977961b | ||
| 
						 | 
					f069c3a595 | ||
| 
						 | 
					b84194e8c7 | ||
| 
						 | 
					e06e989aa5 | ||
| 
						 | 
					ab75505437 | ||
| 
						 | 
					e97663b58e | ||
| 
						 | 
					a92f57a30c | ||
| 
						 | 
					ebb9381e46 | ||
| 
						 | 
					5fb441fb81 | ||
| 
						 | 
					f3a00fe8a2 | ||
| 
						 | 
					573494570c | ||
| 
						 | 
					92392f5c9a | ||
| 
						 | 
					8a634a0d95 | ||
| 
						 | 
					23c5d48b5d | ||
| 
						 | 
					f000b344dd | ||
| 
						 | 
					42177e1ff2 | ||
| 
						 | 
					091ca483e7 | ||
| 
						 | 
					42ce4f8821 | ||
| 
						 | 
					9a1781765b | ||
| 
						 | 
					544b8181e5 | ||
| 
						 | 
					82ea37ead9 | ||
| 
						 | 
					882ef93084 | ||
| 
						 | 
					881469ab7f | ||
| 
						 | 
					d7834650ae | ||
| 
						 | 
					20bf183a10 | ||
| 
						 | 
					c990ac66da | ||
| 
						 | 
					0964163a35 | ||
| 
						 | 
					fc980b1bc9 | ||
| 
						 | 
					e76c8a3ba7 | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,11 +1,13 @@
 | 
			
		||||
*~
 | 
			
		||||
debian/files
 | 
			
		||||
debian/libgbinder-dev
 | 
			
		||||
debian/libgbinder
 | 
			
		||||
debian/libgbinder-dev
 | 
			
		||||
debian/libgbinder-tools
 | 
			
		||||
debian/*.debhelper.log
 | 
			
		||||
debian/*.debhelper
 | 
			
		||||
debian/*.substvars
 | 
			
		||||
debian/*.install
 | 
			
		||||
debian/libgbinder.install
 | 
			
		||||
debian/libgbinder-dev.install
 | 
			
		||||
debian/tmp
 | 
			
		||||
documentation.list
 | 
			
		||||
installroot
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								Makefile
									
									
									
									
									
								
							@@ -15,8 +15,8 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 0
 | 
			
		||||
VERSION_RELEASE = 43
 | 
			
		||||
VERSION_MINOR = 1
 | 
			
		||||
VERSION_RELEASE = 6
 | 
			
		||||
 | 
			
		||||
# Version for pkg-config
 | 
			
		||||
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
 | 
			
		||||
@@ -75,9 +75,11 @@ LIB = $(LIB_NAME).a
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
SRC = \
 | 
			
		||||
  gbinder_bridge.c \
 | 
			
		||||
  gbinder_buffer.c \
 | 
			
		||||
  gbinder_cleanup.c \
 | 
			
		||||
  gbinder_client.c \
 | 
			
		||||
  gbinder_config.c \
 | 
			
		||||
  gbinder_driver.c \
 | 
			
		||||
  gbinder_eventloop.c \
 | 
			
		||||
  gbinder_io_32.c \
 | 
			
		||||
@@ -87,6 +89,7 @@ SRC = \
 | 
			
		||||
  gbinder_local_reply.c \
 | 
			
		||||
  gbinder_local_request.c \
 | 
			
		||||
  gbinder_log.c \
 | 
			
		||||
  gbinder_proxy_object.c \
 | 
			
		||||
  gbinder_reader.c \
 | 
			
		||||
  gbinder_remote_object.c \
 | 
			
		||||
  gbinder_remote_reply.c \
 | 
			
		||||
@@ -97,9 +100,10 @@ SRC = \
 | 
			
		||||
  gbinder_writer.c
 | 
			
		||||
 | 
			
		||||
SRC += \
 | 
			
		||||
  gbinder_defaultservicemanager.c \
 | 
			
		||||
  gbinder_hwservicemanager.c \
 | 
			
		||||
  gbinder_servicemanager.c
 | 
			
		||||
  gbinder_servicemanager.c \
 | 
			
		||||
  gbinder_servicemanager_aidl.c \
 | 
			
		||||
  gbinder_servicemanager_aidl2.c \
 | 
			
		||||
  gbinder_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
SRC += \
 | 
			
		||||
  gbinder_system.c
 | 
			
		||||
@@ -231,7 +235,7 @@ clean:
 | 
			
		||||
	rm -fr debian/tmp debian/libgbinder debian/libgbinder-dev
 | 
			
		||||
	rm -f documentation.list debian/files debian/*.substvars
 | 
			
		||||
	rm -f debian/*.debhelper.log debian/*.debhelper debian/*~
 | 
			
		||||
	rm -f debian/*.install
 | 
			
		||||
	rm -f debian/libgbinder.install debian/libgbinder-dev.install
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
	make -C unit test
 | 
			
		||||
@@ -319,7 +323,7 @@ INSTALL_INCLUDE_DIR = $(DESTDIR)/usr/include/$(NAME)
 | 
			
		||||
INSTALL_PKGCONFIG_DIR = $(DESTDIR)$(ABS_LIBDIR)/pkgconfig
 | 
			
		||||
 | 
			
		||||
install: $(INSTALL_LIB_DIR)
 | 
			
		||||
	$(INSTALL_FILES) $(RELEASE_SO) $(INSTALL_LIB_DIR)
 | 
			
		||||
	$(INSTALL) -m 755 $(RELEASE_SO) $(INSTALL_LIB_DIR)
 | 
			
		||||
	ln -sf $(LIB_SO) $(INSTALL_LIB_DIR)/$(LIB_SYMLINK2)
 | 
			
		||||
	ln -sf $(LIB_SYMLINK2) $(INSTALL_LIB_DIR)/$(LIB_SYMLINK1)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								README
									
									
									
									
									
								
							@@ -1,7 +1,44 @@
 | 
			
		||||
GLib-style interface to binder (Android IPC mechanism)
 | 
			
		||||
 | 
			
		||||
Provides:
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
Android keeps changing both low-level RPC and service manager
 | 
			
		||||
protocols from version to version. To counter that, libgbinder
 | 
			
		||||
implements configirable backends for different variants of those,
 | 
			
		||||
and yet keeping its own API unchanged.
 | 
			
		||||
 | 
			
		||||
Configuration is loaded from [Protocol] and [ServiceManager] sections
 | 
			
		||||
of /etc/gbinder.conf file. The keys are binder device names or the
 | 
			
		||||
special Default value, the value is the identifier of the protocol
 | 
			
		||||
or service manager variant, respectively.
 | 
			
		||||
 | 
			
		||||
In addition to reading /etc/gbinder.conf if it exists, /etc/gbinder.d
 | 
			
		||||
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
 | 
			
		||||
configuration is as follows:
 | 
			
		||||
 | 
			
		||||
  [Protocol]
 | 
			
		||||
  Default = aidl
 | 
			
		||||
  /dev/binder = aidl
 | 
			
		||||
  /dev/hwbinder = hidl
 | 
			
		||||
 | 
			
		||||
  [ServiceManager]
 | 
			
		||||
  Default = aidl
 | 
			
		||||
  /dev/binder = aidl
 | 
			
		||||
  /dev/hwbinder = hidl
 | 
			
		||||
 | 
			
		||||
Alternatively, one can specify the desired Android API level:
 | 
			
		||||
 | 
			
		||||
  [General]
 | 
			
		||||
  ApiLevel = 29
 | 
			
		||||
 | 
			
		||||
and let libgbinder pick the appropriate preset.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										92
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,95 @@
 | 
			
		||||
libgbinder (1.1.6) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Implemented support for passing object over the bridge
 | 
			
		||||
  * Retry service name registration
 | 
			
		||||
  * Wait for completion of the reply
 | 
			
		||||
  * Fixed death handling by GBinderBridge
 | 
			
		||||
  * Added gbinder_bridge_new2()
 | 
			
		||||
  * Added -s option to binder-bridge
 | 
			
		||||
  * Fixed invalid slice deallocation
 | 
			
		||||
  * Made unit tests more reliable
 | 
			
		||||
  * Make sure that libgbinder doesn't block on exit
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 02 Mar 2021 18:18:03 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.5) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed gbinder_remote_reply_copy_to_local() for empty replies
 | 
			
		||||
  * Improved binder simulation
 | 
			
		||||
  * Added GBinderBridge object
 | 
			
		||||
  * Added proxy_object and bridge unit tests
 | 
			
		||||
  * Added binder-bridge to libgbinder-tools package
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 29 Jan 2021 04:00:09 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.4) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed a threading issue
 | 
			
		||||
  * Decode NULL object reference
 | 
			
		||||
  * Added new basic HIDL types
 | 
			
		||||
  * Set TF_ACCEPT_FDS transaction flag
 | 
			
		||||
  * Added servicemanager_hidl unit test
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 21 Jan 2021 03:34:45 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.3) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Improved unit test coverage
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 23 Dec 2020 21:48:27 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.2) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed random unit text failures
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 23 Dec 2020 12:39:22 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.1) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Handle corner cases for abandoned loopers
 | 
			
		||||
  * Pass 0x0f priority to aidl2 service list request.
 | 
			
		||||
  * Improved binder simulation for unit tests
 | 
			
		||||
  * Added servicemanager_aidl unit test
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 22 Dec 2020 15:15:10 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.0) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Made RPC protocol configurable per binder device
 | 
			
		||||
  * Made service managers configurable per binder device
 | 
			
		||||
  * Added support for multiple config files
 | 
			
		||||
  * Added "aidl2" variant of service manager
 | 
			
		||||
  * Added "aidl2" variant of RPC protocol
 | 
			
		||||
  * Added support for API level presets
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 04 Dec 2020 13:47:26 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.47) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Make library executable on RPM based systems
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Sat, 19 Sep 2020 20:14:20 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.46) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Make sure we drop fds that are going to be closed
 | 
			
		||||
  * Better diagnostics for polling failures
 | 
			
		||||
  * Package binder-list and binder-ping utilities
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 31 Jul 2020 02:04:38 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.45) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Always provide data buffer for hidl vector
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 27 Jul 2020 23:19:25 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.44) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Never drop valid incoming transactions
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 29 Jun 2020 17:05:22 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.43) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Hide internal symbols
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -16,3 +16,9 @@ Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: libgbinder (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: Development files for libgbinder
 | 
			
		||||
 | 
			
		||||
Package: libgbinder-tools
 | 
			
		||||
Section: utils
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: libgbinder, ${misc:Depends}
 | 
			
		||||
Description: Binder command line utilities
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/libgbinder-tools.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libgbinder-tools.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
debian/tmp/usr/bin/* usr/bin
 | 
			
		||||
							
								
								
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -8,9 +8,15 @@ 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-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-list
 | 
			
		||||
	dh_auto_install -- -C test/binder-ping
 | 
			
		||||
 | 
			
		||||
%:
 | 
			
		||||
	dh $@
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,6 +35,7 @@
 | 
			
		||||
 | 
			
		||||
/* Convenience header to pull in everything at once */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_bridge.h"
 | 
			
		||||
#include "gbinder_buffer.h"
 | 
			
		||||
#include "gbinder_client.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										89
									
								
								include/gbinder_bridge.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								include/gbinder_bridge.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_BRIDGE_H
 | 
			
		||||
#define GBINDER_BRIDGE_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types.h"
 | 
			
		||||
 | 
			
		||||
/* Since 1.1.5 */
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * A binder bridge object.
 | 
			
		||||
 *
 | 
			
		||||
 * For example, bridging "foobar" with interfaces ["example@1.0::IFoo",
 | 
			
		||||
 * "example@1.0::IBar"] would:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Watch "example@1.0::IFoo/foobar" and "example@1.0::IBar/foobar" on dest
 | 
			
		||||
 * 2. When those names appears, register objects with the same name on src
 | 
			
		||||
 * 3. Pass calls coming from src to the dest objects and replies in the
 | 
			
		||||
 *    opposite direction
 | 
			
		||||
 * 4. When dest objects disappear, remove the corresponding bridging objects
 | 
			
		||||
 *    from src
 | 
			
		||||
 *
 | 
			
		||||
 * and so on.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
GBinderBridge*
 | 
			
		||||
gbinder_bridge_new(
 | 
			
		||||
    const char* name,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderServiceManager* src,
 | 
			
		||||
    GBinderServiceManager* dest) /* Since 1.1.5 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderBridge*
 | 
			
		||||
gbinder_bridge_new2(
 | 
			
		||||
    const char* src_name,
 | 
			
		||||
    const char* dest_name,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderServiceManager* src,
 | 
			
		||||
    GBinderServiceManager* dest) /* Since 1.1.6 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_free(
 | 
			
		||||
    GBinderBridge* bridge); /* Since 1.1.5 */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_BRIDGE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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
 | 
			
		||||
@@ -80,25 +80,29 @@ gbinder_servicemanager_new(
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev);
 | 
			
		||||
    const char* dev)
 | 
			
		||||
    G_DEPRECATED_FOR(gbinder_servicemanager_new);
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev);
 | 
			
		||||
    const char* dev)
 | 
			
		||||
    G_DEPRECATED_FOR(gbinder_servicemanager_new);
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_servicemanager_new_local_object(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_servicemanager_new_local_object2(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data); /* Since 1.0.29 */
 | 
			
		||||
    void* user_data) /* Since 1.0.29 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_ref(
 | 
			
		||||
@@ -125,7 +129,9 @@ gbinder_servicemanager_list(
 | 
			
		||||
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_list_sync(
 | 
			
		||||
    GBinderServiceManager* sm);
 | 
			
		||||
    GBinderServiceManager* sm)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT
 | 
			
		||||
    G_GNUC_MALLOC;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_get_service(
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -59,6 +59,7 @@ G_BEGIN_DECLS
 | 
			
		||||
 * 6. Reader parses the data coming with RemoteRequest and RemoteReply
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_bridge GBinderBridge; /* Since 1.1.5 */
 | 
			
		||||
typedef struct gbinder_buffer GBinderBuffer;
 | 
			
		||||
typedef struct gbinder_client GBinderClient;
 | 
			
		||||
typedef struct gbinder_ipc GBinderIpc;
 | 
			
		||||
@@ -76,16 +77,20 @@ typedef struct gbinder_parent GBinderParent;
 | 
			
		||||
 | 
			
		||||
/* Basic HIDL types */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_ALIGNED(x) __attribute__ ((aligned(x)))
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_vec {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const void* ptr;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 count;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[3];
 | 
			
		||||
} GBinderHidlVec;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_VEC_BUFFER_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlVec) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_string {
 | 
			
		||||
    union {
 | 
			
		||||
@@ -93,10 +98,51 @@ typedef struct gbinder_hidl_string {
 | 
			
		||||
        const char* str;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 len;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[3];
 | 
			
		||||
} GBinderHidlString;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_STRING_BUFFER_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlString) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_fds {
 | 
			
		||||
    guint32 version GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 num_fds GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 num_ints GBINDER_ALIGNED(4);
 | 
			
		||||
} GBINDER_ALIGNED(4) GBinderFds;  /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
/* Actual fds immediately follow GBinderFds: */
 | 
			
		||||
#define gbinder_fds_get_fd(fds,i) (((const int*)((fds) + 1))[i])
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_FDS_VERSION (12)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderFds) == GBINDER_HIDL_FDS_VERSION);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_handle {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const GBinderFds* fds;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint8 owns_handle;
 | 
			
		||||
    guint8 pad[7];
 | 
			
		||||
} GBinderHidlHandle; /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_HANDLE_VALUE_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlHandle) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_memory {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const GBinderFds* fds;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[7];
 | 
			
		||||
    guint64 size;
 | 
			
		||||
    GBinderHidlString name;
 | 
			
		||||
} GBinderHidlMemory; /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_MEMORY_PTR_OFFSET (0)
 | 
			
		||||
#define GBINDER_HIDL_MEMORY_NAME_OFFSET (24)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlMemory) == 40);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Each RPC call is identified by the interface name returned
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
Name: libgbinder
 | 
			
		||||
Version: 1.0.43
 | 
			
		||||
Version: 1.1.6
 | 
			
		||||
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.35
 | 
			
		||||
%define libglibutil_version 1.0.49
 | 
			
		||||
 | 
			
		||||
BuildRequires: pkgconfig(glib-2.0)
 | 
			
		||||
BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version}
 | 
			
		||||
@@ -30,10 +30,16 @@ This package contains the development library for %{name}.
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
make 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
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
rm -rf %{buildroot}
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make -C unit test
 | 
			
		||||
@@ -51,3 +57,18 @@ make -C unit test
 | 
			
		||||
%{_libdir}/pkgconfig/*.pc
 | 
			
		||||
%{_libdir}/%{name}.so
 | 
			
		||||
%{_includedir}/gbinder/*.h
 | 
			
		||||
 | 
			
		||||
# Tools
 | 
			
		||||
 | 
			
		||||
%package tools
 | 
			
		||||
Summary: Binder tools
 | 
			
		||||
Requires: %{name} >= %{version}
 | 
			
		||||
 | 
			
		||||
%description tools
 | 
			
		||||
Binder command line utilities
 | 
			
		||||
 | 
			
		||||
%files tools
 | 
			
		||||
%defattr(-,root,root,-)
 | 
			
		||||
%{_bindir}/binder-bridge
 | 
			
		||||
%{_bindir}/binder-list
 | 
			
		||||
%{_bindir}/binder-ping
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										272
									
								
								src/gbinder_bridge.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								src/gbinder_bridge.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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_local_request.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_proxy_object.h"
 | 
			
		||||
#include "gbinder_remote_request_p.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_bridge.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_bridge_interface {
 | 
			
		||||
    GBinderBridge* bridge;
 | 
			
		||||
    char* iface;
 | 
			
		||||
    char* fqname;
 | 
			
		||||
    char* src_name;
 | 
			
		||||
    char* dest_name;
 | 
			
		||||
    gulong dest_watch_id;
 | 
			
		||||
    gulong dest_death_id;
 | 
			
		||||
    GBinderRemoteObject* dest_obj;
 | 
			
		||||
    GBinderServiceName* src_service;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
} GBinderBridgeInterface;
 | 
			
		||||
 | 
			
		||||
struct gbinder_bridge {
 | 
			
		||||
    GBinderBridgeInterface** ifaces;
 | 
			
		||||
    GBinderServiceManager* src;
 | 
			
		||||
    GBinderServiceManager* dest;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_dest_drop_remote_object(
 | 
			
		||||
    GBinderBridgeInterface* bi)
 | 
			
		||||
{
 | 
			
		||||
    if (bi->dest_obj) {
 | 
			
		||||
        GDEBUG("Detached from %s", bi->fqname);
 | 
			
		||||
        gbinder_remote_object_remove_handler(bi->dest_obj, bi->dest_death_id);
 | 
			
		||||
        gbinder_remote_object_unref(bi->dest_obj);
 | 
			
		||||
        bi->dest_death_id = 0;
 | 
			
		||||
        bi->dest_obj = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_interface_deactivate(
 | 
			
		||||
    GBinderBridgeInterface* bi)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_bridge_dest_drop_remote_object(bi);
 | 
			
		||||
    if (bi->proxy) {
 | 
			
		||||
        gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(bi->proxy));
 | 
			
		||||
        bi->proxy = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (bi->src_service) {
 | 
			
		||||
        gbinder_servicename_unref(bi->src_service);
 | 
			
		||||
        bi->src_service = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_interface_free(
 | 
			
		||||
    GBinderBridgeInterface* bi)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBridge* bridge = bi->bridge;
 | 
			
		||||
 | 
			
		||||
    gbinder_bridge_interface_deactivate(bi);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(bridge->dest, bi->dest_watch_id);
 | 
			
		||||
    g_free(bi->iface);
 | 
			
		||||
    g_free(bi->fqname);
 | 
			
		||||
    g_free(bi->src_name);
 | 
			
		||||
    g_free(bi->dest_name);
 | 
			
		||||
    gutil_slice_free(bi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_dest_death_proc(
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBridgeInterface* bi = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("%s has died", bi->fqname);
 | 
			
		||||
    gbinder_bridge_interface_deactivate(bi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_interface_activate(
 | 
			
		||||
    GBinderBridgeInterface* bi)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBridge* bridge = bi->bridge;
 | 
			
		||||
    GBinderServiceManager* src = bridge->src;
 | 
			
		||||
    GBinderServiceManager* dest = bridge->dest;
 | 
			
		||||
 | 
			
		||||
    if (bi->dest_obj && bi->dest_obj->dead) {
 | 
			
		||||
        gbinder_bridge_dest_drop_remote_object(bi);
 | 
			
		||||
    }
 | 
			
		||||
    if (!bi->dest_obj) {
 | 
			
		||||
        bi->dest_obj = gbinder_servicemanager_get_service_sync(dest,
 | 
			
		||||
            bi->fqname, NULL);
 | 
			
		||||
        if (bi->dest_obj) {
 | 
			
		||||
            GDEBUG("Attached to %s", bi->fqname);
 | 
			
		||||
            gbinder_remote_object_ref(bi->dest_obj);
 | 
			
		||||
            bi->dest_death_id = gbinder_remote_object_add_death_handler
 | 
			
		||||
                (bi->dest_obj, gbinder_bridge_dest_death_proc, bi);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (bi->dest_obj && !bi->proxy) {
 | 
			
		||||
        bi->proxy = gbinder_proxy_object_new(gbinder_servicemanager_ipc(src),
 | 
			
		||||
            bi->dest_obj);
 | 
			
		||||
    }
 | 
			
		||||
    if (bi->proxy && !bi->src_service) {
 | 
			
		||||
        bi->src_service = gbinder_servicename_new(src,
 | 
			
		||||
            GBINDER_LOCAL_OBJECT(bi->proxy), bi->src_name);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_dest_registration_proc(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBridgeInterface* bi = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(name, bi->fqname)) {
 | 
			
		||||
        GDEBUG("%s has been registered", bi->fqname);
 | 
			
		||||
        gbinder_bridge_interface_activate(bi);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderBridgeInterface*
 | 
			
		||||
gbinder_bridge_interface_new(
 | 
			
		||||
    GBinderBridge* self,
 | 
			
		||||
    const char* src_name,
 | 
			
		||||
    const char* dest_name,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBridgeInterface* bi = g_slice_new0(GBinderBridgeInterface);
 | 
			
		||||
 | 
			
		||||
    bi->bridge = self;
 | 
			
		||||
    bi->iface = g_strdup(iface);
 | 
			
		||||
    bi->fqname = g_strconcat(iface, "/", dest_name, NULL);
 | 
			
		||||
    bi->src_name = g_strdup(src_name);
 | 
			
		||||
    bi->dest_name = g_strdup(dest_name);
 | 
			
		||||
    bi->dest_watch_id = gbinder_servicemanager_add_registration_handler
 | 
			
		||||
        (self->dest, bi->fqname, gbinder_bridge_dest_registration_proc, bi);
 | 
			
		||||
 | 
			
		||||
    /* Try to activate at startup */
 | 
			
		||||
    gbinder_bridge_interface_activate(bi);
 | 
			
		||||
    return bi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderBridge*
 | 
			
		||||
gbinder_bridge_new(
 | 
			
		||||
    const char* name,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderServiceManager* src,
 | 
			
		||||
    GBinderServiceManager* dest) /* Since 1.1.5 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_bridge_new2(name, NULL, ifaces, src, dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBridge*
 | 
			
		||||
gbinder_bridge_new2(
 | 
			
		||||
    const char* src_name,
 | 
			
		||||
    const char* dest_name,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderServiceManager* src,
 | 
			
		||||
    GBinderServiceManager* dest) /* Since 1.1.6 */
 | 
			
		||||
{
 | 
			
		||||
    const guint n = gutil_strv_length((const GStrV*)ifaces);
 | 
			
		||||
 | 
			
		||||
    if (!src_name) {
 | 
			
		||||
        src_name = dest_name;
 | 
			
		||||
    } else if (!dest_name) {
 | 
			
		||||
        dest_name = src_name;
 | 
			
		||||
    }
 | 
			
		||||
    if (G_LIKELY(src_name) && G_LIKELY(n) && G_LIKELY(src) && G_LIKELY(dest)) {
 | 
			
		||||
        GBinderBridge* self = g_slice_new0(GBinderBridge);
 | 
			
		||||
        guint i;
 | 
			
		||||
 | 
			
		||||
        self->src = gbinder_servicemanager_ref(src);
 | 
			
		||||
        self->dest = gbinder_servicemanager_ref(dest);
 | 
			
		||||
        self->ifaces = g_new(GBinderBridgeInterface*, n + 1);
 | 
			
		||||
        for (i = 0; i < n; i++) {
 | 
			
		||||
            self->ifaces[i] = gbinder_bridge_interface_new(self,
 | 
			
		||||
                src_name, dest_name, ifaces[i]);
 | 
			
		||||
        }
 | 
			
		||||
        self->ifaces[i] = NULL;
 | 
			
		||||
        return self;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_bridge_free(
 | 
			
		||||
    GBinderBridge* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderBridgeInterface** bi = self->ifaces;
 | 
			
		||||
 | 
			
		||||
        while (*bi) {
 | 
			
		||||
            gbinder_bridge_interface_free(*bi);
 | 
			
		||||
            bi++;
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_unref(self->src);
 | 
			
		||||
        gbinder_servicemanager_unref(self->dest);
 | 
			
		||||
        g_free(self->ifaces);
 | 
			
		||||
        gutil_slice_free(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -112,6 +112,47 @@ gbinder_buffer_contents_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderBufferContentsList
 | 
			
		||||
 * It's actually a GSList containing GBinderBufferContents refs.
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderBufferContentsList*
 | 
			
		||||
gbinder_buffer_contents_list_add(
 | 
			
		||||
    GBinderBufferContentsList* list,
 | 
			
		||||
    GBinderBufferContents* contents)
 | 
			
		||||
{
 | 
			
		||||
    /* Prepend rather than append for better efficiency */
 | 
			
		||||
    return contents ? (GBinderBufferContentsList*) g_slist_prepend((GSList*)
 | 
			
		||||
            list, gbinder_buffer_contents_ref(contents)) : list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBufferContentsList*
 | 
			
		||||
gbinder_buffer_contents_list_dup(
 | 
			
		||||
    GBinderBufferContentsList* list)
 | 
			
		||||
{
 | 
			
		||||
    GSList* out = NULL;
 | 
			
		||||
 | 
			
		||||
    if (list) {
 | 
			
		||||
        GSList* l = (GSList*) list;
 | 
			
		||||
 | 
			
		||||
        /* The order gets reversed but it doesn't matter */
 | 
			
		||||
        while (l) {
 | 
			
		||||
            out = g_slist_prepend(out, gbinder_buffer_contents_ref(l->data));
 | 
			
		||||
            l = l->next;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return (GBinderBufferContentsList*) out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_buffer_contents_list_free(
 | 
			
		||||
    GBinderBufferContentsList* list)
 | 
			
		||||
{
 | 
			
		||||
    g_slist_free_full((GSList*) list, (GDestroyNotify)
 | 
			
		||||
        gbinder_buffer_contents_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderBuffer
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -88,6 +88,22 @@ gbinder_buffer_contents_unref(
 | 
			
		||||
    GBinderBufferContents* contents)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderBufferContentsList*
 | 
			
		||||
gbinder_buffer_contents_list_add(
 | 
			
		||||
    GBinderBufferContentsList* list,
 | 
			
		||||
    GBinderBufferContents* contents)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderBufferContentsList*
 | 
			
		||||
gbinder_buffer_contents_list_dup(
 | 
			
		||||
    GBinderBufferContentsList* list)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_buffer_contents_list_free(
 | 
			
		||||
    GBinderBufferContentsList* list)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_BUFFER_PRIVATE_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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -178,6 +178,66 @@ gbinder_client_transact_destroy(
 | 
			
		||||
    g_slice_free(GBinderClientTx, tx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internal interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_client_transact_sync_reply2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return api->sync_reply(obj->ipc, obj->handle, code, req, status);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_client_transact_sync_oneway2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return api->sync_oneway(obj->ipc, obj->handle, code, req);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
        return (-ESTALE);
 | 
			
		||||
    }
 | 
			
		||||
    return (-EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -319,25 +379,8 @@ gbinder_client_transact_sync_reply(
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
 | 
			
		||||
                code, req, status);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
    return gbinder_client_transact_sync_reply2(self, code, req, status,
 | 
			
		||||
        &gbinder_ipc_sync_main);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -346,26 +389,8 @@ gbinder_client_transact_sync_oneway(
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
 | 
			
		||||
                code, req);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
        return (-ESTALE);
 | 
			
		||||
    }
 | 
			
		||||
    return (-EINVAL);
 | 
			
		||||
    return gbinder_client_transact_sync_oneway2(self, code, req,
 | 
			
		||||
        &gbinder_ipc_sync_main);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -41,6 +41,24 @@ struct gbinder_client {
 | 
			
		||||
    GBinderRemoteObject* remote;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_client_transact_sync_reply2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_client_transact_sync_oneway2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#define gbinder_client_ipc(client) ((client)->remote->ipc)
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_CLIENT_PRIVATE_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										384
									
								
								src/gbinder_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								src/gbinder_config.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,384 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 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_config.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The contents of the config file is queried from (at least) two places,
 | 
			
		||||
 * and pretty much always this happens the same stack. Which means that
 | 
			
		||||
 * we can avoid reading the same file twice if we delay dereferencing of
 | 
			
		||||
 * GKeyFile until the next idle loop.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static GKeyFile* gbinder_config_keyfile = NULL;
 | 
			
		||||
static GBinderEventLoopCallback* gbinder_config_autorelease = NULL;
 | 
			
		||||
 | 
			
		||||
static const char gbinder_config_suffix[] = ".conf";
 | 
			
		||||
static const char gbinder_config_default_file[] = "/etc/gbinder.conf";
 | 
			
		||||
static const char gbinder_config_default_dir[] = "/etc/gbinder.d";
 | 
			
		||||
 | 
			
		||||
const char* gbinder_config_file = gbinder_config_default_file;
 | 
			
		||||
const char* gbinder_config_dir = gbinder_config_default_dir;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Presets for particular API level can be chosen with ApiLevel
 | 
			
		||||
 * setting, e.g.
 | 
			
		||||
 *
 | 
			
		||||
 * [General]
 | 
			
		||||
 * ApiLevel=29
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static const char CONF_GENERAL[] = "General";
 | 
			
		||||
static const char CONG_API_LEVEL[] = "ApiLevel";
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_config_preset_entry {
 | 
			
		||||
    const char* key;
 | 
			
		||||
    const char* value;
 | 
			
		||||
} GBinderConfigPresetEntry;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_config_preset_group {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const GBinderConfigPresetEntry* entries;
 | 
			
		||||
} GBinderConfigPresetGroup;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_config_preset {
 | 
			
		||||
    guint api_level;
 | 
			
		||||
    const GBinderConfigPresetGroup* groups;
 | 
			
		||||
} GBinderConfigPreset;
 | 
			
		||||
 | 
			
		||||
/* API level 28 */
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetEntry gbinder_config_28_servicemanager[] = {
 | 
			
		||||
    { "/dev/binder", "aidl2" },
 | 
			
		||||
    { "/dev/vndbinder", "aidl2" },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetGroup gbinder_config_28[] = {
 | 
			
		||||
    { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_28_servicemanager },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* API level 29 */
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetEntry gbinder_config_29_protocol[] = {
 | 
			
		||||
    { "/dev/binder", "aidl2" },
 | 
			
		||||
    { "/dev/vndbinder", "aidl2" },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define gbinder_config_29_servicemanager gbinder_config_28_servicemanager
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPresetGroup gbinder_config_29[] = {
 | 
			
		||||
    { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_29_protocol },
 | 
			
		||||
    { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_29_servicemanager },
 | 
			
		||||
    { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Presets sorted by API level in descending order */
 | 
			
		||||
 | 
			
		||||
static const GBinderConfigPreset gbinder_config_presets[] = {
 | 
			
		||||
    { 29, gbinder_config_29 },
 | 
			
		||||
    { 28, gbinder_config_28 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_config_collect_files(
 | 
			
		||||
    const char* path,
 | 
			
		||||
    const char* suffix)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * Returns sorted list of regular files in the directory,
 | 
			
		||||
     * optionally having the specified suffix (e.g. ".conf").
 | 
			
		||||
     * Returns NULL if nothing appropriate has been found.
 | 
			
		||||
     */
 | 
			
		||||
    char** files = NULL;
 | 
			
		||||
 | 
			
		||||
    if (path) {
 | 
			
		||||
        GDir* dir = g_dir_open(path, 0, NULL);
 | 
			
		||||
 | 
			
		||||
        if (dir) {
 | 
			
		||||
            GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
            const gchar* name;
 | 
			
		||||
 | 
			
		||||
            while ((name = g_dir_read_name(dir)) != NULL) {
 | 
			
		||||
                if (g_str_has_suffix(name, suffix)) {
 | 
			
		||||
                    char* fullname = g_build_filename(path, name, NULL);
 | 
			
		||||
                    struct stat st;
 | 
			
		||||
 | 
			
		||||
                    if (!stat(fullname, &st) && S_ISREG(st.st_mode)) {
 | 
			
		||||
                        g_ptr_array_add(list, fullname);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        g_free(fullname);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (list->len > 0) {
 | 
			
		||||
                g_ptr_array_add(list, NULL);
 | 
			
		||||
                files = (char**) g_ptr_array_free(list, FALSE);
 | 
			
		||||
                gutil_strv_sort(files, TRUE);
 | 
			
		||||
            } else {
 | 
			
		||||
                g_ptr_array_free(list, TRUE);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            g_dir_close(dir);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return files;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GKeyFile*
 | 
			
		||||
gbinder_config_merge_keyfiles(
 | 
			
		||||
    GKeyFile* dest,
 | 
			
		||||
    GKeyFile* src)
 | 
			
		||||
{
 | 
			
		||||
    gsize i, ngroups;
 | 
			
		||||
    gchar** groups = g_key_file_get_groups(src, &ngroups);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ngroups; i++) {
 | 
			
		||||
        gsize k, nkeys;
 | 
			
		||||
        const char* group = groups[i];
 | 
			
		||||
        char** keys = g_key_file_get_keys(src, group, &nkeys, NULL);
 | 
			
		||||
 | 
			
		||||
        for (k = 0; k < nkeys; k++) {
 | 
			
		||||
            const char* key = keys[k];
 | 
			
		||||
            char* value = g_key_file_get_value(src, group, key, NULL);
 | 
			
		||||
 | 
			
		||||
            g_key_file_set_value(dest, group, key, value);
 | 
			
		||||
            g_free(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g_strfreev(keys);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_strfreev(groups);
 | 
			
		||||
    return dest;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_config_apply_presets(
 | 
			
		||||
    GKeyFile* config,
 | 
			
		||||
    const GBinderConfigPreset* preset)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderConfigPresetGroup* g;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Applying presets for API level %d", preset->api_level);
 | 
			
		||||
    for (g = preset->groups; g->name; g++) {
 | 
			
		||||
        const GBinderConfigPresetEntry* e;
 | 
			
		||||
 | 
			
		||||
        for (e = g->entries; e->key; e++) {
 | 
			
		||||
            if (!g_key_file_has_key(config, g->name, e->key, NULL)) {
 | 
			
		||||
                g_key_file_set_value(config, g->name, e->key, e->value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GKeyFile*
 | 
			
		||||
gbinder_config_load_files()
 | 
			
		||||
{
 | 
			
		||||
    GError* error = NULL;
 | 
			
		||||
    GKeyFile* out = NULL;
 | 
			
		||||
    char** files = gbinder_config_collect_files(gbinder_config_dir,
 | 
			
		||||
        gbinder_config_suffix);
 | 
			
		||||
 | 
			
		||||
    if (gbinder_config_file &&
 | 
			
		||||
        g_file_test(gbinder_config_file, G_FILE_TEST_EXISTS)) {
 | 
			
		||||
        out = g_key_file_new();
 | 
			
		||||
        if (g_key_file_load_from_file(out, gbinder_config_file,
 | 
			
		||||
            G_KEY_FILE_NONE, &error)) {
 | 
			
		||||
            GDEBUG("Loaded %s", gbinder_config_file);
 | 
			
		||||
        } else {
 | 
			
		||||
            GERR("Error loading %s: %s", gbinder_config_file, error->message);
 | 
			
		||||
            g_error_free(error);
 | 
			
		||||
            error = NULL;
 | 
			
		||||
            gbinder_config_file = NULL; /* Don't retry */
 | 
			
		||||
            g_key_file_unref(out);
 | 
			
		||||
            out = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Files in the config directory overwrite /etc/gbinder.conf */
 | 
			
		||||
    if (files) {
 | 
			
		||||
        char** ptr;
 | 
			
		||||
        GKeyFile* override = NULL;
 | 
			
		||||
 | 
			
		||||
        for (ptr = files; *ptr; ptr++) {
 | 
			
		||||
            const char* file = *ptr;
 | 
			
		||||
 | 
			
		||||
            if (!override) {
 | 
			
		||||
                override = g_key_file_new();
 | 
			
		||||
            }
 | 
			
		||||
            if (g_key_file_load_from_file(override, file,
 | 
			
		||||
                G_KEY_FILE_NONE, &error)) {
 | 
			
		||||
                GDEBUG("Loaded %s", file);
 | 
			
		||||
                if (!out) {
 | 
			
		||||
                    out = override;
 | 
			
		||||
                    override = NULL;
 | 
			
		||||
                } else {
 | 
			
		||||
                    out = gbinder_config_merge_keyfiles(out, override);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                GERR("Error loading %s: %s", file, error->message);
 | 
			
		||||
                g_error_free(error);
 | 
			
		||||
                error = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g_strfreev(files);
 | 
			
		||||
        if (override) {
 | 
			
		||||
            g_key_file_unref(override);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (out) {
 | 
			
		||||
        /* Apply presets */
 | 
			
		||||
        const int api_level = g_key_file_get_integer(out,
 | 
			
		||||
            CONF_GENERAL, CONG_API_LEVEL, NULL);
 | 
			
		||||
 | 
			
		||||
        if (api_level > 0) {
 | 
			
		||||
            int i;
 | 
			
		||||
 | 
			
		||||
            GDEBUG("API level %d", api_level);
 | 
			
		||||
            for (i = 0; i < G_N_ELEMENTS(gbinder_config_presets); i++) {
 | 
			
		||||
                const GBinderConfigPreset* preset = gbinder_config_presets + i;
 | 
			
		||||
 | 
			
		||||
                if (api_level >= preset->api_level) {
 | 
			
		||||
                    gbinder_config_apply_presets(out, preset);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_config_autorelease_cb(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GASSERT(gbinder_config_keyfile == data);
 | 
			
		||||
    gbinder_config_keyfile = NULL;
 | 
			
		||||
    g_key_file_unref(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GKeyFile* /* autoreleased */
 | 
			
		||||
gbinder_config_get()
 | 
			
		||||
{
 | 
			
		||||
    if (!gbinder_config_keyfile &&
 | 
			
		||||
        (gbinder_config_file || gbinder_config_dir)) {
 | 
			
		||||
        gbinder_config_keyfile = gbinder_config_load_files();
 | 
			
		||||
        if (gbinder_config_keyfile) {
 | 
			
		||||
            /* See the comment at the top of the file why this is needed */
 | 
			
		||||
            gbinder_config_autorelease = gbinder_idle_callback_schedule_new
 | 
			
		||||
               (gbinder_config_autorelease_cb, gbinder_config_keyfile, NULL);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return gbinder_config_keyfile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper for loading config group in device = ident format */
 | 
			
		||||
GHashTable*
 | 
			
		||||
gbinder_config_load(
 | 
			
		||||
    const char* group,
 | 
			
		||||
    GBinderConfigValueMapFunc mapper)
 | 
			
		||||
{
 | 
			
		||||
    GKeyFile* k = gbinder_config_get();
 | 
			
		||||
    GHashTable* map = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        g_free, NULL);
 | 
			
		||||
 | 
			
		||||
    if (k) {
 | 
			
		||||
        gsize n;
 | 
			
		||||
        char** devs = g_key_file_get_keys(k, group, &n, NULL);
 | 
			
		||||
 | 
			
		||||
        if (devs) {
 | 
			
		||||
            gsize i;
 | 
			
		||||
 | 
			
		||||
            for (i = 0; i < n; i++) {
 | 
			
		||||
                char* dev = devs[i];
 | 
			
		||||
                char* sval = g_key_file_get_value(k, group, dev, NULL);
 | 
			
		||||
                gconstpointer val = mapper(sval);
 | 
			
		||||
 | 
			
		||||
                if (val) {
 | 
			
		||||
                    g_hash_table_replace(map, dev, (gpointer) val);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GWARN("Unknown gbinder config '%s' for %s in group [%s]",
 | 
			
		||||
                        sval, dev, group);
 | 
			
		||||
                    g_free(dev);
 | 
			
		||||
                }
 | 
			
		||||
                g_free(sval);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Shallow delete (contents got stolen or freed) */
 | 
			
		||||
            g_free(devs);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_config_exit()
 | 
			
		||||
{
 | 
			
		||||
    if (gbinder_config_autorelease) {
 | 
			
		||||
        gbinder_idle_callback_destroy(gbinder_config_autorelease);
 | 
			
		||||
        gbinder_config_autorelease = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (gbinder_config_keyfile) {
 | 
			
		||||
        g_key_file_unref(gbinder_config_keyfile);
 | 
			
		||||
        gbinder_config_keyfile = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										78
									
								
								src/gbinder_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/gbinder_config.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_CONFIG_H
 | 
			
		||||
#define GBINDER_CONFIG_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
gconstpointer
 | 
			
		||||
(*GBinderConfigValueMapFunc)(
 | 
			
		||||
    const char* value);
 | 
			
		||||
 | 
			
		||||
GHashTable*
 | 
			
		||||
gbinder_config_load(
 | 
			
		||||
    const char* group,
 | 
			
		||||
    GBinderConfigValueMapFunc map)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GKeyFile* /* autoreleased */
 | 
			
		||||
gbinder_config_get(
 | 
			
		||||
    void)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* This one declared strictly for unit tests */
 | 
			
		||||
void
 | 
			
		||||
gbinder_config_exit(
 | 
			
		||||
    void)
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    GBINDER_DESTRUCTOR;
 | 
			
		||||
 | 
			
		||||
/* And these too */
 | 
			
		||||
extern const char* gbinder_config_file GBINDER_INTERNAL;
 | 
			
		||||
extern const char* gbinder_config_dir GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Configuration groups and special value */
 | 
			
		||||
#define GBINDER_CONFIG_GROUP_PROTOCOL "Protocol"
 | 
			
		||||
#define GBINDER_CONFIG_GROUP_SERVICEMANAGER "ServiceManager"
 | 
			
		||||
#define GBINDER_CONFIG_VALUE_DEFAULT "Default"
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_CONFIG_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,321 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_defaultservicemanager_watch {
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    char* name;
 | 
			
		||||
    gulong handler_id;
 | 
			
		||||
    GBinderEventLoopTimeout* notify;
 | 
			
		||||
} GBinderDefaultServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderDefaultServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_defaultservicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderDefaultServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderDefaultServiceManager,
 | 
			
		||||
    gbinder_defaultservicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_defaultservicemanager_parent_class
 | 
			
		||||
#define GBINDER_TYPE_DEFAULTSERVICEMANAGER \
 | 
			
		||||
    gbinder_defaultservicemanager_get_type()
 | 
			
		||||
#define GBINDER_DEFAULTSERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_DEFAULTSERVICEMANAGER, \
 | 
			
		||||
    GBinderDefaultServiceManager)
 | 
			
		||||
 | 
			
		||||
enum gbinder_defaultservicemanager_calls {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define DEFAULTSERVICEMANAGER_IFACE  "android.os.IServiceManager"
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type
 | 
			
		||||
        (GBINDER_TYPE_DEFAULTSERVICEMANAGER, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_watch_proc(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(name_added, watch->name)) {
 | 
			
		||||
        GBinderServiceManager* manager =
 | 
			
		||||
            gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
 | 
			
		||||
        if (watch->notify) {
 | 
			
		||||
            gbinder_timeout_remove(watch->notify);
 | 
			
		||||
            watch->notify = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_service_registered(manager, name_added);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_defaultservicemanager_watch_notify(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
    GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
    char* name = g_strdup(watch->name);
 | 
			
		||||
 | 
			
		||||
    GASSERT(watch->notify);
 | 
			
		||||
    watch->notify = NULL;
 | 
			
		||||
    gbinder_servicemanager_service_registered(manager, name);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_watch_free(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_timeout_remove(watch->notify);
 | 
			
		||||
    gbinder_servicepoll_remove_handler(watch->poll, watch->handler_id);
 | 
			
		||||
    gbinder_servicepoll_unref(watch->poll);
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    g_slice_free(GBinderDefaultServiceManagerWatch, watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderDefaultServiceManagerWatch*
 | 
			
		||||
gbinder_defaultservicemanager_watch_new(
 | 
			
		||||
    GBinderDefaultServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch =
 | 
			
		||||
        g_slice_new0(GBinderDefaultServiceManagerWatch);
 | 
			
		||||
 | 
			
		||||
    watch->name = g_strdup(name);
 | 
			
		||||
    watch->poll = gbinder_servicepoll_new(&manager->manager, &manager->poll);
 | 
			
		||||
    watch->handler_id = gbinder_servicepoll_add_handler(watch->poll,
 | 
			
		||||
        gbinder_defaultservicemanager_watch_proc, watch);
 | 
			
		||||
    return watch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_list_services_req(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    gint32 index)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_local_request_append_int32
 | 
			
		||||
        (gbinder_client_new_request(self->client), index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_defaultservicemanager_list(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_servicemanager_list_services_req(self,0);
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
 | 
			
		||||
    while ((reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        LIST_SERVICES_TRANSACTION, req, NULL)) != NULL) {
 | 
			
		||||
        char* service = gbinder_remote_reply_read_string16(reply);
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_unref(reply);
 | 
			
		||||
        if (service) {
 | 
			
		||||
            g_ptr_array_add(list, service);
 | 
			
		||||
            gbinder_local_request_unref(req);
 | 
			
		||||
            req = gbinder_servicemanager_list_services_req(self, list->len);
 | 
			
		||||
        } else {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_ptr_array_add(list, NULL);
 | 
			
		||||
    return (char**)g_ptr_array_free(list, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_defaultservicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* obj;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        CHECK_SERVICE_TRANSACTION, req, status);
 | 
			
		||||
 | 
			
		||||
    obj = gbinder_remote_reply_read_object(reply);
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_defaultservicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj);
 | 
			
		||||
    gbinder_local_request_append_int32(req, 0);
 | 
			
		||||
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        ADD_SERVICE_TRANSACTION, req, &status);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
gbinder_defaultservicemanager_check_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_defaultservicemanager_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(manager);
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch =
 | 
			
		||||
        gbinder_defaultservicemanager_watch_new(self, name);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_replace(self->watch_table, watch->name, watch);
 | 
			
		||||
    if (gbinder_servicepoll_is_known_name(watch->poll, name)) {
 | 
			
		||||
        watch->notify = gbinder_idle_add
 | 
			
		||||
            (gbinder_defaultservicemanager_watch_notify, watch);
 | 
			
		||||
    }
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_remove(GBINDER_DEFAULTSERVICEMANAGER(manager)->watch_table,
 | 
			
		||||
        name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_init(
 | 
			
		||||
    GBinderDefaultServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        NULL, gbinder_defaultservicemanager_watch_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->watch_table);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_class_init(
 | 
			
		||||
    GBinderDefaultServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = DEFAULTSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
 | 
			
		||||
    klass->list = gbinder_defaultservicemanager_list;
 | 
			
		||||
    klass->get_service = gbinder_defaultservicemanager_get_service;
 | 
			
		||||
    klass->add_service = gbinder_defaultservicemanager_add_service;
 | 
			
		||||
    klass->check_name = gbinder_defaultservicemanager_check_name;
 | 
			
		||||
    /* normalize_name is not needed */
 | 
			
		||||
    klass->watch = gbinder_defaultservicemanager_watch;
 | 
			
		||||
    klass->unwatch = gbinder_defaultservicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_defaultservicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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,6 +32,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_cleanup.h"
 | 
			
		||||
#include "gbinder_handler.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
@@ -84,10 +85,30 @@ struct gbinder_driver {
 | 
			
		||||
    const GBinderRpcProtocol* protocol;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_io_read_buf {
 | 
			
		||||
    GBinderIoBuf buf;
 | 
			
		||||
typedef struct gbinder_driver_read_buf {
 | 
			
		||||
    GBinderIoBuf io;
 | 
			
		||||
    gsize offset;
 | 
			
		||||
} GBinderDriverReadBuf;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_driver_read_data {
 | 
			
		||||
    GBinderDriverReadBuf buf;
 | 
			
		||||
    guint8 data[GBINDER_IO_READ_BUFFER_SIZE];
 | 
			
		||||
} GBinderIoReadBuf;
 | 
			
		||||
} GBinderDriverReadData;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_driver_context {
 | 
			
		||||
    GBinderDriverReadBuf* rbuf;
 | 
			
		||||
    GBinderObjectRegistry* reg;
 | 
			
		||||
    GBinderHandler* handler;
 | 
			
		||||
    GBinderCleanup* unrefs;
 | 
			
		||||
    GBinderBufferContentsList* bufs;
 | 
			
		||||
} GBinderDriverContext;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_txstatus(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderDriverContext* context,
 | 
			
		||||
    GBinderRemoteReply* reply);
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
@@ -140,18 +161,38 @@ gbinder_driver_verbose_transaction_data(
 | 
			
		||||
            guint n = 0;
 | 
			
		||||
            while (tx->objects[n]) n++;
 | 
			
		||||
            if (tx->status) {
 | 
			
		||||
                GVERBOSE("> %s %d (%u bytes, %u objects)", name, tx->status,
 | 
			
		||||
                    (guint)tx->size, n);
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p %d (%u bytes, %u objects)", name,
 | 
			
		||||
                             tx->target, tx->status, (guint)tx->size, n);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s %d (%u bytes, %u objects)", name,
 | 
			
		||||
                        tx->status, (guint)tx->size, n);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                GVERBOSE("> %s (%u bytes, %u objects)", name,
 | 
			
		||||
                    (guint)tx->size, n);
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p (%u bytes, %u objects)", name,
 | 
			
		||||
                        tx->target, (guint)tx->size, n);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s (%u bytes, %u objects)", name, (guint)
 | 
			
		||||
                        tx->size, n);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (tx->status) {
 | 
			
		||||
                GVERBOSE("> %s %d (%u bytes)", name, tx->status,
 | 
			
		||||
                    (guint)tx->size);
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p %d (%u bytes)", name, tx->target,
 | 
			
		||||
                        tx->status, (guint)tx->size);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s %d (%u bytes)", name, tx->status, (guint)
 | 
			
		||||
                        tx->size);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                GVERBOSE("> %s (%u bytes)", name, (guint)tx->size);
 | 
			
		||||
                if (tx->target) {
 | 
			
		||||
                    GVERBOSE("> %s %p (%u bytes)", name, tx->target, (guint)
 | 
			
		||||
                        tx->size);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GVERBOSE("> %s (%u bytes)", name, (guint)tx->size);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -175,9 +216,11 @@ gbinder_driver_write(
 | 
			
		||||
        gbinder_driver_verbose_dump('<',
 | 
			
		||||
            buf->ptr +  buf->consumed,
 | 
			
		||||
            buf->size - buf->consumed);
 | 
			
		||||
        GVERBOSE_("%u/%u", (guint)buf->consumed, (guint)buf->size);
 | 
			
		||||
        GVERBOSE("gbinder_driver_write(%d) %u/%u", self->fd,
 | 
			
		||||
            (guint)buf->consumed, (guint)buf->size);
 | 
			
		||||
        err = self->io->write_read(self->fd, buf, NULL);
 | 
			
		||||
        GVERBOSE_("%u/%u err %d", (guint)buf->consumed, (guint)buf->size, err);
 | 
			
		||||
        GVERBOSE("gbinder_driver_write(%d) %u/%u err %d", self->fd,
 | 
			
		||||
            (guint)buf->consumed, (guint)buf->size, err);
 | 
			
		||||
    }
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
@@ -187,11 +230,24 @@ int
 | 
			
		||||
gbinder_driver_write_read(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderIoBuf* write,
 | 
			
		||||
    GBinderIoBuf* read)
 | 
			
		||||
    GBinderDriverReadBuf* rbuf)
 | 
			
		||||
{
 | 
			
		||||
    int err = (-EAGAIN);
 | 
			
		||||
    GBinderIoBuf rio;
 | 
			
		||||
    GBinderIoBuf* read;
 | 
			
		||||
 | 
			
		||||
    /* rbuf is never NULL */
 | 
			
		||||
    if (rbuf->offset) {
 | 
			
		||||
        rio.ptr = rbuf->io.ptr + rbuf->offset;
 | 
			
		||||
        rio.size = rbuf->io.size - rbuf->offset;
 | 
			
		||||
        rio.consumed = rbuf->io.consumed - rbuf->offset;
 | 
			
		||||
        read = &rio;
 | 
			
		||||
    } else {
 | 
			
		||||
        read = &rbuf->io;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (err == (-EAGAIN)) {
 | 
			
		||||
 | 
			
		||||
#if GUTIL_LOG_VERBOSE
 | 
			
		||||
        const gsize were_consumed = read ? read->consumed : 0;
 | 
			
		||||
        if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) {
 | 
			
		||||
@@ -200,21 +256,23 @@ gbinder_driver_write_read(
 | 
			
		||||
                    write->ptr +  write->consumed,
 | 
			
		||||
                    write->size - write->consumed);
 | 
			
		||||
            }
 | 
			
		||||
            GVERBOSE_("write %u/%u read %u/%u",
 | 
			
		||||
              (guint)(write ? write->consumed : 0),
 | 
			
		||||
              (guint)(write ? write->size : 0),
 | 
			
		||||
              (guint)(read ? read->consumed : 0),
 | 
			
		||||
              (guint)(read ? read->size : 0));
 | 
			
		||||
            GVERBOSE("gbinder_driver_write_read(%d) "
 | 
			
		||||
                "write %u/%u read %u/%u", self->fd,
 | 
			
		||||
                (guint)(write ? write->consumed : 0),
 | 
			
		||||
                (guint)(write ? write->size : 0),
 | 
			
		||||
                (guint)(read ? read->consumed : 0),
 | 
			
		||||
                (guint)(read ? read->size : 0));
 | 
			
		||||
        }
 | 
			
		||||
#endif /* GUTIL_LOG_VERBOSE */
 | 
			
		||||
        err = self->io->write_read(self->fd, write, read);
 | 
			
		||||
#if GUTIL_LOG_VERBOSE
 | 
			
		||||
        if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) {
 | 
			
		||||
            GVERBOSE_("write %u/%u read %u/%u err %d",
 | 
			
		||||
              (guint)(write ? write->consumed : 0),
 | 
			
		||||
              (guint)(write ? write->size : 0),
 | 
			
		||||
              (guint)(read ? read->consumed : 0),
 | 
			
		||||
              (guint)(read ? read->size : 0), err);
 | 
			
		||||
            GVERBOSE("gbinder_driver_write_read(%d) "
 | 
			
		||||
                "write %u/%u read %u/%u err %d", self->fd,
 | 
			
		||||
                (guint)(write ? write->consumed : 0),
 | 
			
		||||
                (guint)(write ? write->size : 0),
 | 
			
		||||
                (guint)(read ? read->consumed : 0),
 | 
			
		||||
                (guint)(read ? read->size : 0), err);
 | 
			
		||||
            if (read) {
 | 
			
		||||
                gbinder_driver_verbose_dump('>',
 | 
			
		||||
                    read->ptr + were_consumed,
 | 
			
		||||
@@ -223,6 +281,10 @@ gbinder_driver_write_read(
 | 
			
		||||
        }
 | 
			
		||||
#endif /* GUTIL_LOG_VERBOSE */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rbuf->offset) {
 | 
			
		||||
        rbuf->io.consumed = rio.consumed + rbuf->offset;
 | 
			
		||||
    }
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -280,55 +342,78 @@ gbinder_driver_cmd_data(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_death_notification(
 | 
			
		||||
gbinder_driver_handle_cookie(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    guint32 cmd,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIoBuf write;
 | 
			
		||||
    guint8 buf[4 + GBINDER_MAX_DEATH_NOTIFICATION_SIZE];
 | 
			
		||||
    guint8 buf[4 + GBINDER_MAX_HANDLE_COOKIE_SIZE];
 | 
			
		||||
    guint32* data = (guint32*)buf;
 | 
			
		||||
 | 
			
		||||
    data[0] = cmd;
 | 
			
		||||
    memset(&write, 0, sizeof(write));
 | 
			
		||||
    write.ptr = (uintptr_t)buf;
 | 
			
		||||
    write.size = 4 + self->io->encode_death_notification(data + 1, obj);
 | 
			
		||||
    write.size = 4 + self->io->encode_handle_cookie(data + 1, obj);
 | 
			
		||||
    return gbinder_driver_write(self, &write) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_read_init(
 | 
			
		||||
    GBinderIoReadBuf* rb)
 | 
			
		||||
    GBinderDriverReadData* read)
 | 
			
		||||
{
 | 
			
		||||
    rb->buf.ptr = (uintptr_t)(rb->data);
 | 
			
		||||
    rb->buf.size = sizeof(rb->data);
 | 
			
		||||
    rb->buf.consumed = 0;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * It shouldn't be necessary to zero-initialize the buffer but
 | 
			
		||||
     * valgrind complains about access to uninitialised data if we
 | 
			
		||||
     * don't do so. Oh well...
 | 
			
		||||
     * It shouldn't be necessary to zero-initialize the whole buffer
 | 
			
		||||
     * but valgrind complains about access to uninitialised data if
 | 
			
		||||
     * we don't do so. Oh well...
 | 
			
		||||
     */
 | 
			
		||||
    memset(rb->data, 0, sizeof(rb->data));
 | 
			
		||||
    memset(read, 0, sizeof(*read));
 | 
			
		||||
    read->buf.io.ptr = GPOINTER_TO_SIZE(read->data);
 | 
			
		||||
    read->buf.io.size = sizeof(read->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_context_init(
 | 
			
		||||
    GBinderDriverContext* context,
 | 
			
		||||
    GBinderDriverReadBuf* rbuf,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler)
 | 
			
		||||
{
 | 
			
		||||
    context->rbuf = rbuf;
 | 
			
		||||
    context->reg = reg;
 | 
			
		||||
    context->handler = handler;
 | 
			
		||||
    context->unrefs = NULL;
 | 
			
		||||
    context->bufs = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_context_cleanup(
 | 
			
		||||
    GBinderDriverContext* context)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_cleanup_free(context->unrefs);
 | 
			
		||||
    gbinder_buffer_contents_list_free(context->bufs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint32
 | 
			
		||||
gbinder_driver_next_command(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    const GBinderIoBuf* buf)
 | 
			
		||||
    const GBinderDriverReadBuf* rbuf)
 | 
			
		||||
{
 | 
			
		||||
    const size_t remaining = buf->size - buf->consumed;
 | 
			
		||||
    guint32 cmd = 0;
 | 
			
		||||
    if (rbuf->io.consumed > rbuf->offset) {
 | 
			
		||||
        const gsize remaining = rbuf->io.consumed - rbuf->offset;
 | 
			
		||||
 | 
			
		||||
    if (remaining >= sizeof(cmd)) {
 | 
			
		||||
        int datalen;
 | 
			
		||||
        /* The size of the data to follow is encoded in the command code */
 | 
			
		||||
        cmd = *(guint32*)(buf->ptr + buf->consumed);
 | 
			
		||||
        datalen = _IOC_SIZE(cmd);
 | 
			
		||||
        if (remaining >= sizeof(cmd) + datalen) {
 | 
			
		||||
            return cmd;
 | 
			
		||||
        if (remaining >= 4) {
 | 
			
		||||
            /* The size of the data to follow is encoded in the command code */
 | 
			
		||||
            const guint32 cmd = *(guint32*)(rbuf->io.ptr + rbuf->offset);
 | 
			
		||||
            const guint datalen = _IOC_SIZE(cmd);
 | 
			
		||||
 | 
			
		||||
            if (remaining >= 4 + datalen) {
 | 
			
		||||
                return cmd;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -377,18 +462,18 @@ gbinder_driver_reply_data(
 | 
			
		||||
    GUtilIntArray* offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    void* offsets_buf = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Build BC_TRANSACTION */
 | 
			
		||||
    /* Build BC_REPLY */
 | 
			
		||||
    if (extra_buffers) {
 | 
			
		||||
        GVERBOSE("< BC_REPLY_SG %u bytes", (guint)extra_buffers);
 | 
			
		||||
        gbinder_driver_verbose_dump_bytes(' ', data->bytes);
 | 
			
		||||
        *cmd = io->bc.reply_sg;
 | 
			
		||||
        len += io->encode_transaction_sg(buf + len, 0, 0, data->bytes, 0,
 | 
			
		||||
        len += io->encode_reply_sg(buf + len, 0, 0, data->bytes,
 | 
			
		||||
            offsets, &offsets_buf, extra_buffers);
 | 
			
		||||
    } else {
 | 
			
		||||
        GVERBOSE("< BC_REPLY");
 | 
			
		||||
        gbinder_driver_verbose_dump_bytes(' ', data->bytes);
 | 
			
		||||
        *cmd = io->bc.reply;
 | 
			
		||||
        len += io->encode_transaction(buf + len, 0, 0, data->bytes, 0,
 | 
			
		||||
        len += io->encode_reply(buf + len, 0, 0, data->bytes,
 | 
			
		||||
            offsets, &offsets_buf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -413,16 +498,16 @@ static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_handle_transaction(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* h,
 | 
			
		||||
    GBinderDriverContext* context,
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderObjectRegistry* reg = context->reg;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    GBinderIoTxData tx;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    int status = -EBADMSG;
 | 
			
		||||
    int txstatus = -EBADMSG;
 | 
			
		||||
 | 
			
		||||
    self->io->decode_transaction_data(data, &tx);
 | 
			
		||||
    gbinder_driver_verbose_transaction_data("BR_TRANSACTION", &tx);
 | 
			
		||||
@@ -431,9 +516,13 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
 | 
			
		||||
    /* Transfer data ownership to the request */
 | 
			
		||||
    if (tx.data && tx.size) {
 | 
			
		||||
        GBinderBuffer* buf = gbinder_buffer_new(self,
 | 
			
		||||
            tx.data, tx.size, tx.objects);
 | 
			
		||||
 | 
			
		||||
        gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size);
 | 
			
		||||
        gbinder_remote_request_set_data(req, tx.code,
 | 
			
		||||
            gbinder_buffer_new(self, tx.data, tx.size, tx.objects));
 | 
			
		||||
        gbinder_remote_request_set_data(req, tx.code, buf);
 | 
			
		||||
        context->bufs = gbinder_buffer_contents_list_add(context->bufs,
 | 
			
		||||
            gbinder_buffer_contents(buf));
 | 
			
		||||
    } else {
 | 
			
		||||
        GASSERT(!tx.objects);
 | 
			
		||||
        gbinder_driver_free_buffer(self, tx.data);
 | 
			
		||||
@@ -444,11 +533,19 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
    switch (gbinder_local_object_can_handle_transaction(obj, iface, tx.code)) {
 | 
			
		||||
    case GBINDER_LOCAL_TRANSACTION_LOOPER:
 | 
			
		||||
        reply = gbinder_local_object_handle_looper_transaction(obj, req,
 | 
			
		||||
            tx.code, tx.flags, &status);
 | 
			
		||||
            tx.code, tx.flags, &txstatus);
 | 
			
		||||
        break;
 | 
			
		||||
    case GBINDER_LOCAL_TRANSACTION_SUPPORTED:
 | 
			
		||||
        reply = gbinder_handler_transact(h, obj, req, tx.code, tx.flags,
 | 
			
		||||
            &status);
 | 
			
		||||
        /*
 | 
			
		||||
         * NULL GBinderHandler means that this is a synchronous call
 | 
			
		||||
         * executed on the main thread, meaning that we can call the
 | 
			
		||||
         * local object directly.
 | 
			
		||||
         */
 | 
			
		||||
        reply = context->handler ?
 | 
			
		||||
            gbinder_handler_transact(context->handler, obj, req, tx.code,
 | 
			
		||||
                tx.flags, &txstatus) :
 | 
			
		||||
            gbinder_local_object_handle_transaction(obj, req, tx.code,
 | 
			
		||||
                tx.flags, &txstatus);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GWARN("Unhandled transaction %s 0x%08x", iface, tx.code);
 | 
			
		||||
@@ -458,10 +555,20 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
    /* No reply for one-way transactions */
 | 
			
		||||
    if (!(tx.flags & GBINDER_TX_FLAG_ONEWAY)) {
 | 
			
		||||
        if (reply) {
 | 
			
		||||
            context->bufs = gbinder_buffer_contents_list_add(context->bufs,
 | 
			
		||||
                gbinder_local_reply_contents(reply));
 | 
			
		||||
            gbinder_driver_reply_data(self, gbinder_local_reply_data(reply));
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_driver_reply_status(self, status);
 | 
			
		||||
            gbinder_driver_reply_status(self, txstatus);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Wait until the reply is handled */
 | 
			
		||||
        do {
 | 
			
		||||
            txstatus = gbinder_driver_write_read(self, NULL, context->rbuf);
 | 
			
		||||
            if (txstatus >= 0) {
 | 
			
		||||
                txstatus = gbinder_driver_txstatus(self, context, NULL);
 | 
			
		||||
            }
 | 
			
		||||
        } while (txstatus == (-EAGAIN));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Free the data allocated for the transaction */
 | 
			
		||||
@@ -470,23 +577,45 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_cleanup_decrefs(
 | 
			
		||||
    gpointer pointer)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(pointer);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_handle_decrefs(obj);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_cleanup_release(
 | 
			
		||||
    gpointer pointer)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(pointer);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_handle_release(obj);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_handle_command(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    GBinderDriverContext* context,
 | 
			
		||||
    guint32 cmd,
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = self->io;
 | 
			
		||||
    GBinderObjectRegistry* reg = context->reg;
 | 
			
		||||
 | 
			
		||||
    if (cmd == io->br.noop) {
 | 
			
		||||
        GVERBOSE("> BR_NOOP");
 | 
			
		||||
    } else if (cmd == io->br.ok) {
 | 
			
		||||
        GVERBOSE("> BR_OK");
 | 
			
		||||
    } else if (cmd == io->br.transaction_complete) {
 | 
			
		||||
        GVERBOSE("> BR_TRANSACTION_COMPLETE");
 | 
			
		||||
        GVERBOSE("> BR_TRANSACTION_COMPLETE (?)");
 | 
			
		||||
    } else if (cmd == io->br.spawn_looper) {
 | 
			
		||||
        GVERBOSE("> BR_SPAWN_LOOPER");
 | 
			
		||||
    } else if (cmd == io->br.finished) {
 | 
			
		||||
@@ -494,7 +623,7 @@ gbinder_driver_handle_command(
 | 
			
		||||
    } else if (cmd == io->br.increfs) {
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
 | 
			
		||||
        GBinderLocalObject* obj = gbinder_object_registry_get_local
 | 
			
		||||
            (reg, io->decode_binder_ptr_cookie(data));
 | 
			
		||||
            (reg, io->decode_ptr_cookie(data));
 | 
			
		||||
 | 
			
		||||
        GVERBOSE("> BR_INCREFS %p", obj);
 | 
			
		||||
        gbinder_local_object_handle_increfs(obj);
 | 
			
		||||
@@ -503,44 +632,66 @@ gbinder_driver_handle_command(
 | 
			
		||||
        gbinder_driver_cmd_data(self, io->bc.increfs_done, data, buf);
 | 
			
		||||
    } else if (cmd == io->br.decrefs) {
 | 
			
		||||
        GBinderLocalObject* obj = gbinder_object_registry_get_local
 | 
			
		||||
            (reg, io->decode_binder_ptr_cookie(data));
 | 
			
		||||
            (reg, io->decode_ptr_cookie(data));
 | 
			
		||||
 | 
			
		||||
        GVERBOSE("> BR_DECREFS %p", obj);
 | 
			
		||||
        gbinder_local_object_handle_decrefs(obj);
 | 
			
		||||
        gbinder_local_object_unref(obj);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            /*
 | 
			
		||||
             * Unrefs must be processed only after clearing the incoming
 | 
			
		||||
             * command queue.
 | 
			
		||||
             */
 | 
			
		||||
            context->unrefs = gbinder_cleanup_add(context->unrefs,
 | 
			
		||||
                gbinder_driver_cleanup_decrefs, obj);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (cmd == io->br.acquire) {
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
 | 
			
		||||
        GBinderLocalObject* obj = gbinder_object_registry_get_local
 | 
			
		||||
            (reg, io->decode_binder_ptr_cookie(data));
 | 
			
		||||
            (reg, io->decode_ptr_cookie(data));
 | 
			
		||||
 | 
			
		||||
        GVERBOSE("> BR_ACQUIRE %p", obj);
 | 
			
		||||
        gbinder_local_object_handle_acquire(obj);
 | 
			
		||||
        gbinder_local_object_unref(obj);
 | 
			
		||||
        GVERBOSE("< BC_ACQUIRE_DONE %p", obj);
 | 
			
		||||
        gbinder_driver_cmd_data(self, io->bc.acquire_done, data, buf);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            /* BC_ACQUIRE_DONE will be sent after the request is handled */
 | 
			
		||||
            gbinder_local_object_handle_acquire(obj, context->bufs);
 | 
			
		||||
            gbinder_local_object_unref(obj);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* This shouldn't normally happen. Just send the same data back. */
 | 
			
		||||
            GVERBOSE("< BC_ACQUIRE_DONE");
 | 
			
		||||
            gbinder_driver_cmd_data(self, io->bc.acquire_done, data, buf);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (cmd == io->br.release) {
 | 
			
		||||
        GBinderLocalObject* obj = gbinder_object_registry_get_local
 | 
			
		||||
            (reg, io->decode_binder_ptr_cookie(data));
 | 
			
		||||
            (reg, io->decode_ptr_cookie(data));
 | 
			
		||||
 | 
			
		||||
        GVERBOSE("> BR_RELEASE %p", obj);
 | 
			
		||||
        gbinder_local_object_handle_release(obj);
 | 
			
		||||
        gbinder_local_object_unref(obj);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            /*
 | 
			
		||||
             * Unrefs must be processed only after clearing the incoming
 | 
			
		||||
             * command queue.
 | 
			
		||||
             */
 | 
			
		||||
            context->unrefs = gbinder_cleanup_add(context->unrefs,
 | 
			
		||||
                gbinder_driver_cleanup_release, obj);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (cmd == io->br.transaction) {
 | 
			
		||||
        gbinder_driver_handle_transaction(self, reg, handler, data);
 | 
			
		||||
        gbinder_driver_handle_transaction(self, context, data);
 | 
			
		||||
    } else if (cmd == io->br.dead_binder) {
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
 | 
			
		||||
        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);
 | 
			
		||||
        obj = gbinder_object_registry_get_remote(reg, (guint32)handle);
 | 
			
		||||
        obj = gbinder_object_registry_get_remote(reg, (guint32)handle,
 | 
			
		||||
            REMOTE_REGISTRY_DONT_CREATE);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            /* BC_DEAD_BINDER_DONE will be sent after the request is handled */
 | 
			
		||||
            gbinder_remote_object_handle_death_notification(obj);
 | 
			
		||||
            gbinder_remote_object_unref(obj);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* This shouldn't normally happen. Just send the same data back. */
 | 
			
		||||
            GVERBOSE("< BC_DEAD_BINDER_DONE %llu", (long long unsigned int)
 | 
			
		||||
                handle);
 | 
			
		||||
            gbinder_driver_cmd_data(self, io->bc.dead_binder_done, data, buf);
 | 
			
		||||
        }
 | 
			
		||||
        GVERBOSE("< BC_DEAD_BINDER_DONE %llu", (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");
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -549,68 +700,73 @@ gbinder_driver_handle_command(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_compact_read_buf(
 | 
			
		||||
    GBinderDriverReadBuf* buf)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * Move the data to the beginning of the buffer to make room for the
 | 
			
		||||
     * next portion of data (in case if we need one)
 | 
			
		||||
     */
 | 
			
		||||
    if (buf->io.consumed > buf->offset) {
 | 
			
		||||
        const gsize unprocessed = buf->io.consumed - buf->offset;
 | 
			
		||||
        guint8* data = GSIZE_TO_POINTER(buf->io.ptr);
 | 
			
		||||
 | 
			
		||||
        memmove(data, data + buf->offset, unprocessed);
 | 
			
		||||
        buf->io.consumed = unprocessed;
 | 
			
		||||
    } else {
 | 
			
		||||
        buf->io.consumed = 0;
 | 
			
		||||
    }
 | 
			
		||||
    buf->offset = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_handle_commands(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    GBinderIoReadBuf* rb)
 | 
			
		||||
    GBinderDriverContext* context)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDriverReadBuf* rbuf = context->rbuf;
 | 
			
		||||
    guint32 cmd;
 | 
			
		||||
    gsize unprocessed;
 | 
			
		||||
    GBinderIoBuf buf;
 | 
			
		||||
 | 
			
		||||
    buf.ptr = rb->buf.ptr;
 | 
			
		||||
    buf.size = rb->buf.consumed;
 | 
			
		||||
    buf.consumed = 0;
 | 
			
		||||
 | 
			
		||||
    while ((cmd = gbinder_driver_next_command(self, &buf)) != 0) {
 | 
			
		||||
        const size_t datalen = _IOC_SIZE(cmd);
 | 
			
		||||
        const size_t total = datalen + sizeof(cmd);
 | 
			
		||||
    while ((cmd = gbinder_driver_next_command(self, rbuf)) != 0) {
 | 
			
		||||
        const gsize datalen = _IOC_SIZE(cmd);
 | 
			
		||||
        const gsize total = datalen + sizeof(cmd);
 | 
			
		||||
        const void* data = GSIZE_TO_POINTER(rbuf->io.ptr + rbuf->offset + 4);
 | 
			
		||||
 | 
			
		||||
        /* Handle this command */
 | 
			
		||||
        gbinder_driver_handle_command(self, reg, handler, cmd,
 | 
			
		||||
            (void*)(buf.ptr + buf.consumed + sizeof(cmd)));
 | 
			
		||||
 | 
			
		||||
        /* Switch to the next packet in the buffer */
 | 
			
		||||
        buf.consumed += total;
 | 
			
		||||
        rbuf->offset += total;
 | 
			
		||||
        gbinder_driver_handle_command(self, context, cmd, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Move the data to the beginning of the buffer to make room for the
 | 
			
		||||
     * next portion of data (in case if we need one) */
 | 
			
		||||
    unprocessed = buf.size - buf.consumed;
 | 
			
		||||
    memmove(rb->data, rb->data + buf.consumed, unprocessed);
 | 
			
		||||
    rb->buf.consumed = unprocessed;
 | 
			
		||||
    gbinder_driver_compact_read_buf(rbuf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_txstatus(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    GBinderIoReadBuf* rb,
 | 
			
		||||
    GBinderDriverContext* context,
 | 
			
		||||
    GBinderRemoteReply* reply)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd;
 | 
			
		||||
    gsize unprocessed;
 | 
			
		||||
    int txstatus = (-EAGAIN);
 | 
			
		||||
    GBinderIoBuf buf;
 | 
			
		||||
    GBinderDriverReadBuf* rbuf = context->rbuf;
 | 
			
		||||
    const guint8* buf = GSIZE_TO_POINTER(rbuf->io.ptr);
 | 
			
		||||
    const GBinderIo* io = self->io;
 | 
			
		||||
 | 
			
		||||
    buf.ptr = rb->buf.ptr;
 | 
			
		||||
    buf.size = rb->buf.consumed;
 | 
			
		||||
    buf.consumed = 0;
 | 
			
		||||
 | 
			
		||||
    while (txstatus == (-EAGAIN) && (cmd =
 | 
			
		||||
        gbinder_driver_next_command(self, &buf)) != 0) {
 | 
			
		||||
        gbinder_driver_next_command(self, context->rbuf)) != 0) {
 | 
			
		||||
        /* The size of the data is encoded in the command code */
 | 
			
		||||
        const size_t datalen = _IOC_SIZE(cmd);
 | 
			
		||||
        const size_t total = datalen + sizeof(cmd);
 | 
			
		||||
        const void* data = (void*)(buf.ptr + buf.consumed + sizeof(cmd));
 | 
			
		||||
        const gsize datalen = _IOC_SIZE(cmd);
 | 
			
		||||
        const gsize total = datalen + sizeof(cmd);
 | 
			
		||||
        const void* data = buf + rbuf->offset + sizeof(cmd);
 | 
			
		||||
 | 
			
		||||
        /* Handle the packet */
 | 
			
		||||
        /* Swallow this packet */
 | 
			
		||||
        rbuf->offset += total;
 | 
			
		||||
 | 
			
		||||
        /* Handle the command */
 | 
			
		||||
        if (cmd == io->br.transaction_complete) {
 | 
			
		||||
            GVERBOSE("> BR_TRANSACTION_COMPLETE");
 | 
			
		||||
            if (!reply) {
 | 
			
		||||
@@ -628,32 +784,44 @@ gbinder_driver_txstatus(
 | 
			
		||||
            io->decode_transaction_data(data, &tx);
 | 
			
		||||
            gbinder_driver_verbose_transaction_data("BR_REPLY", &tx);
 | 
			
		||||
 | 
			
		||||
            /* Transfer data ownership to the request */
 | 
			
		||||
            /* Transfer data ownership to the reply */
 | 
			
		||||
            if (tx.data && tx.size) {
 | 
			
		||||
                GBinderBuffer* buf = gbinder_buffer_new(self,
 | 
			
		||||
                    tx.data, tx.size, tx.objects);
 | 
			
		||||
 | 
			
		||||
                gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size);
 | 
			
		||||
                gbinder_remote_reply_set_data(reply,
 | 
			
		||||
                    gbinder_buffer_new(self, tx.data, tx.size, tx.objects));
 | 
			
		||||
                gbinder_remote_reply_set_data(reply, buf);
 | 
			
		||||
                context->bufs = gbinder_buffer_contents_list_add(context->bufs,
 | 
			
		||||
                    gbinder_buffer_contents(buf));
 | 
			
		||||
            } else {
 | 
			
		||||
                GASSERT(!tx.objects);
 | 
			
		||||
                gbinder_driver_free_buffer(self, tx.data);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            txstatus = tx.status;
 | 
			
		||||
            GASSERT(txstatus != (-EAGAIN));
 | 
			
		||||
            if (txstatus == (-EAGAIN)) txstatus = (-EFAULT);
 | 
			
		||||
            /*
 | 
			
		||||
             * Filter out special cases. It's a bit unfortunate that
 | 
			
		||||
             * libgbinder API historically mixed TF_STATUS_CODE payload
 | 
			
		||||
             * with special delivery errors. It's not a bit deal though,
 | 
			
		||||
             * because in real life TF_STATUS_CODE transactions are not
 | 
			
		||||
             * being used that often, if at all.
 | 
			
		||||
             */
 | 
			
		||||
            switch (tx.status) {
 | 
			
		||||
            case (-EAGAIN):
 | 
			
		||||
            case GBINDER_STATUS_FAILED:
 | 
			
		||||
            case GBINDER_STATUS_DEAD_OBJECT:
 | 
			
		||||
                txstatus = (-EFAULT);
 | 
			
		||||
                GWARN("Replacing tx status %d with %d", tx.status, txstatus);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                txstatus = tx.status;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_driver_handle_command(self, reg, handler, cmd, data);
 | 
			
		||||
            gbinder_driver_handle_command(self, context, cmd, data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Switch to the next packet in the buffer */
 | 
			
		||||
        buf.consumed += total;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Move the data to the beginning of the buffer to make room for the
 | 
			
		||||
     * next portion of data (in case if we need one) */
 | 
			
		||||
    unprocessed = buf.size - buf.consumed;
 | 
			
		||||
    memmove(rb->data, rb->data + buf.consumed, unprocessed);
 | 
			
		||||
    rb->buf.consumed = unprocessed;
 | 
			
		||||
    gbinder_driver_compact_read_buf(rbuf);
 | 
			
		||||
    return txstatus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -707,7 +875,7 @@ gbinder_driver_new(
 | 
			
		||||
                            max_threads, strerror(errno));
 | 
			
		||||
                    }
 | 
			
		||||
                    /* Choose the protocol based on the device name
 | 
			
		||||
                     * if none is explicitely specified */
 | 
			
		||||
                     * if none is explicitly specified */
 | 
			
		||||
                    self->protocol = protocol ? protocol :
 | 
			
		||||
                        gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
                    return self;
 | 
			
		||||
@@ -740,11 +908,23 @@ gbinder_driver_unref(
 | 
			
		||||
{
 | 
			
		||||
    GASSERT(self->refcount > 0);
 | 
			
		||||
    if (g_atomic_int_dec_and_test(&self->refcount)) {
 | 
			
		||||
        gbinder_driver_close(self);
 | 
			
		||||
        g_free(self->dev);
 | 
			
		||||
        g_slice_free(GBinderDriver, self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_close(
 | 
			
		||||
    GBinderDriver* self)
 | 
			
		||||
{
 | 
			
		||||
    if (self->vm) {
 | 
			
		||||
        GDEBUG("Closing %s", self->dev);
 | 
			
		||||
        gbinder_system_munmap(self->vm, self->vmsize);
 | 
			
		||||
        gbinder_system_close(self->fd);
 | 
			
		||||
        g_free(self->dev);
 | 
			
		||||
        g_slice_free(GBinderDriver, self);
 | 
			
		||||
        self->fd = -1;
 | 
			
		||||
        self->vm = NULL;
 | 
			
		||||
        self->vmsize = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -803,6 +983,55 @@ gbinder_driver_io(
 | 
			
		||||
    return self->io;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_driver_protocol(
 | 
			
		||||
    GBinderDriver* self)
 | 
			
		||||
{
 | 
			
		||||
    return self->protocol;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_acquire_done(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIoBuf write;
 | 
			
		||||
    guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
 | 
			
		||||
    guint32* data = (guint32*)buf;
 | 
			
		||||
    const GBinderIo* io = self->io;
 | 
			
		||||
 | 
			
		||||
    data[0] = io->bc.acquire_done;
 | 
			
		||||
    memset(&write, 0, sizeof(write));
 | 
			
		||||
    write.ptr = (uintptr_t)buf;
 | 
			
		||||
    write.size = 4 + io->encode_ptr_cookie(data + 1, obj);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("< BC_ACQUIRE_DONE %p", obj);
 | 
			
		||||
    return gbinder_driver_write(self, &write) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_dead_binder_done(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GBinderIoBuf write;
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE];
 | 
			
		||||
        guint32* data = (guint32*)buf;
 | 
			
		||||
        const GBinderIo* io = self->io;
 | 
			
		||||
 | 
			
		||||
        data[0] = io->bc.dead_binder_done;
 | 
			
		||||
        memset(&write, 0, sizeof(write));
 | 
			
		||||
        write.ptr = (uintptr_t)buf;
 | 
			
		||||
        write.size = 4 + io->encode_cookie(data + 1, obj->handle);
 | 
			
		||||
 | 
			
		||||
        GVERBOSE("< BC_DEAD_BINDER_DONE %u", obj->handle);
 | 
			
		||||
        return gbinder_driver_write(self, &write) >= 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_request_death_notification(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
@@ -810,7 +1039,7 @@ gbinder_driver_request_death_notification(
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GVERBOSE("< BC_REQUEST_DEATH_NOTIFICATION 0x%08x", obj->handle);
 | 
			
		||||
        return gbinder_driver_death_notification(self,
 | 
			
		||||
        return gbinder_driver_handle_cookie(self,
 | 
			
		||||
            self->io->bc.request_death_notification, obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
@@ -824,7 +1053,7 @@ gbinder_driver_clear_death_notification(
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GVERBOSE("< BC_CLEAR_DEATH_NOTIFICATION 0x%08x", obj->handle);
 | 
			
		||||
        return gbinder_driver_death_notification(self,
 | 
			
		||||
        return gbinder_driver_handle_cookie(self,
 | 
			
		||||
            self->io->bc.clear_death_notification, obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
@@ -939,23 +1168,26 @@ gbinder_driver_read(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIoReadBuf rb;
 | 
			
		||||
    GBinderDriverReadData read;
 | 
			
		||||
    GBinderDriverContext context;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    gbinder_driver_read_init(&rb);
 | 
			
		||||
    ret = gbinder_driver_write_read(self, NULL, &rb.buf);
 | 
			
		||||
    gbinder_driver_read_init(&read);
 | 
			
		||||
    gbinder_driver_context_init(&context, &read.buf, reg, handler);
 | 
			
		||||
    ret = gbinder_driver_write_read(self, NULL, context.rbuf);
 | 
			
		||||
    if (ret >= 0) {
 | 
			
		||||
        /* Loop until we have handled all the incoming commands */
 | 
			
		||||
        gbinder_driver_handle_commands(self, reg, handler, &rb);
 | 
			
		||||
        while (rb.buf.consumed) {
 | 
			
		||||
            ret = gbinder_driver_write_read(self, NULL, &rb.buf);
 | 
			
		||||
        gbinder_driver_handle_commands(self, &context);
 | 
			
		||||
        while (read.buf.io.consumed && gbinder_handler_can_loop(handler)) {
 | 
			
		||||
            ret = gbinder_driver_write_read(self, NULL, context.rbuf);
 | 
			
		||||
            if (ret >= 0) {
 | 
			
		||||
                gbinder_driver_handle_commands(self, reg, handler, &rb);
 | 
			
		||||
                gbinder_driver_handle_commands(self, &context);
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_driver_context_cleanup(&context);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -963,13 +1195,16 @@ int
 | 
			
		||||
gbinder_driver_transact(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    GBinderRemoteReply* reply)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDriverReadData read;
 | 
			
		||||
    GBinderDriverContext context;
 | 
			
		||||
    GBinderIoBuf write;
 | 
			
		||||
    GBinderIoReadBuf rb;
 | 
			
		||||
    GBinderDriverReadBuf* rbuf = &read.buf;
 | 
			
		||||
    const GBinderIo* io = self->io;
 | 
			
		||||
    const guint flags = reply ? 0 : GBINDER_TX_FLAG_ONEWAY;
 | 
			
		||||
    GBinderOutputData* data = gbinder_local_request_data(req);
 | 
			
		||||
@@ -981,7 +1216,8 @@ gbinder_driver_transact(
 | 
			
		||||
    guint len = sizeof(*cmd);
 | 
			
		||||
    int txstatus = (-EAGAIN);
 | 
			
		||||
 | 
			
		||||
    gbinder_driver_read_init(&rb);
 | 
			
		||||
    gbinder_driver_read_init(&read);
 | 
			
		||||
    gbinder_driver_context_init(&context, &read.buf, reg, handler);
 | 
			
		||||
 | 
			
		||||
    /* Build BC_TRANSACTION */
 | 
			
		||||
    if (extra_buffers) {
 | 
			
		||||
@@ -1015,11 +1251,11 @@ gbinder_driver_transact(
 | 
			
		||||
     * negative is a driver error (except for -EAGAIN meaning that there's
 | 
			
		||||
     * no status yet) */
 | 
			
		||||
    while (txstatus == (-EAGAIN)) {
 | 
			
		||||
        int err = gbinder_driver_write_read(self, &write, &rb.buf);
 | 
			
		||||
        int err = gbinder_driver_write_read(self, &write, rbuf);
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
            txstatus = err;
 | 
			
		||||
        } else {
 | 
			
		||||
            txstatus = gbinder_driver_txstatus(self, reg, NULL, &rb, reply);
 | 
			
		||||
            txstatus = gbinder_driver_txstatus(self, &context, reply);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1028,54 +1264,40 @@ gbinder_driver_transact(
 | 
			
		||||
        GASSERT(write.consumed == write.size || txstatus > 0);
 | 
			
		||||
 | 
			
		||||
        /* Loop until we have handled all the incoming commands */
 | 
			
		||||
        gbinder_driver_handle_commands(self, reg, NULL, &rb);
 | 
			
		||||
        while (rb.buf.consumed) {
 | 
			
		||||
            int err = gbinder_driver_write_read(self, NULL, &rb.buf);
 | 
			
		||||
        gbinder_driver_handle_commands(self, &context);
 | 
			
		||||
        while (rbuf->io.consumed) {
 | 
			
		||||
            int err = gbinder_driver_write_read(self, NULL, rbuf);
 | 
			
		||||
            if (err < 0) {
 | 
			
		||||
                txstatus = err;
 | 
			
		||||
                break;
 | 
			
		||||
            } else {
 | 
			
		||||
                gbinder_driver_handle_commands(self, reg, NULL, &rb);
 | 
			
		||||
                gbinder_driver_handle_commands(self, &context);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_driver_context_cleanup(&context);
 | 
			
		||||
    g_free(offsets_buf);
 | 
			
		||||
    return txstatus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_ping(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderRpcProtocol* protocol = self->protocol;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(self->io, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    protocol->write_ping(&writer);
 | 
			
		||||
    ret = gbinder_driver_transact(self, reg, handle, protocol->ping_tx,
 | 
			
		||||
        req, reply);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_driver_local_request_new(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_local_request_new_iface(self->io, self->protocol, iface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_driver_local_request_new_ping(
 | 
			
		||||
    GBinderDriver* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(self->io, NULL);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    self->protocol->write_rpc_header(&writer, iface);
 | 
			
		||||
    self->protocol->write_ping(&writer);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -53,6 +53,11 @@ gbinder_driver_unref(
 | 
			
		||||
    GBinderDriver* driver)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_close(
 | 
			
		||||
    GBinderDriver* driver)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_fd(
 | 
			
		||||
    GBinderDriver* driver)
 | 
			
		||||
@@ -74,6 +79,23 @@ gbinder_driver_io(
 | 
			
		||||
    GBinderDriver* driver)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_driver_protocol(
 | 
			
		||||
    GBinderDriver* driver)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_acquire_done(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_dead_binder_done(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_driver_request_death_notification(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
@@ -112,7 +134,7 @@ gbinder_driver_release(
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_close_fds(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    void** objects,
 | 
			
		||||
    const void* end)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
@@ -144,23 +166,22 @@ int
 | 
			
		||||
gbinder_driver_transact(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* request,
 | 
			
		||||
    GBinderRemoteReply* reply)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_ping(
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_driver_local_request_new(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    const char* iface)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_driver_local_request_new(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
gbinder_driver_local_request_new_ping(
 | 
			
		||||
    GBinderDriver* self)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_DRIVER_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_handler_functions {
 | 
			
		||||
    gboolean (*can_loop)(GBinderHandler* handler);
 | 
			
		||||
    GBinderLocalReply* (*transact)(GBinderHandler* handler,
 | 
			
		||||
        GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code,
 | 
			
		||||
        guint flags, int* status);
 | 
			
		||||
@@ -47,6 +48,14 @@ struct gbinder_handler {
 | 
			
		||||
 | 
			
		||||
/* Inline wrappers */
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_handler_can_loop(
 | 
			
		||||
    GBinderHandler* self)
 | 
			
		||||
{
 | 
			
		||||
    return self && self->f->can_loop && self->f->can_loop(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_handler_transact(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										181
									
								
								src/gbinder_io.c
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								src/gbinder_io.c
									
									
									
									
									
								
							@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -90,19 +90,49 @@ GBINDER_IO_FN(write_read)(
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns size of the object */
 | 
			
		||||
static
 | 
			
		||||
gsize
 | 
			
		||||
GBINDER_IO_FN(object_size)(
 | 
			
		||||
    const void* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        const struct binder_object_header* hdr = obj;
 | 
			
		||||
 | 
			
		||||
        switch (hdr->type) {
 | 
			
		||||
        case BINDER_TYPE_BINDER:
 | 
			
		||||
        case BINDER_TYPE_WEAK_BINDER:
 | 
			
		||||
        case BINDER_TYPE_HANDLE:
 | 
			
		||||
        case BINDER_TYPE_WEAK_HANDLE:
 | 
			
		||||
            return sizeof(struct flat_binder_object);
 | 
			
		||||
        case BINDER_TYPE_FD:
 | 
			
		||||
            return sizeof(struct binder_fd_object);
 | 
			
		||||
        case BINDER_TYPE_FDA:
 | 
			
		||||
            return sizeof(struct binder_fd_array_object);
 | 
			
		||||
        case BINDER_TYPE_PTR:
 | 
			
		||||
            return sizeof(struct binder_buffer_object);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns size of the object's extra data */
 | 
			
		||||
static
 | 
			
		||||
gsize
 | 
			
		||||
GBINDER_IO_FN(object_data_size)(
 | 
			
		||||
    const void* obj)
 | 
			
		||||
{
 | 
			
		||||
    const struct binder_buffer_object* buf = obj;
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        const struct binder_object_header* hdr = obj;
 | 
			
		||||
 | 
			
		||||
    if (buf && buf->hdr.type == BINDER_TYPE_PTR) {
 | 
			
		||||
        return buf->length;
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0;
 | 
			
		||||
        switch (hdr->type) {
 | 
			
		||||
        case BINDER_TYPE_PTR:
 | 
			
		||||
            return ((struct binder_buffer_object*)obj)->length;
 | 
			
		||||
        case BINDER_TYPE_FDA:
 | 
			
		||||
            return ((struct binder_fd_array_object*)obj)->num_fds * 4;
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Writes pointer to the buffer */
 | 
			
		||||
@@ -118,7 +148,20 @@ GBINDER_IO_FN(encode_pointer)(
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes flat_buffer_object */
 | 
			
		||||
/* Writes cookie to the buffer */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_cookie)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    guint64 cookie)
 | 
			
		||||
{
 | 
			
		||||
    binder_uintptr_t* dest = out;
 | 
			
		||||
 | 
			
		||||
    *dest = (uintptr_t)cookie;
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes flat_binder_object */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_local_object)(
 | 
			
		||||
@@ -163,12 +206,12 @@ GBINDER_IO_FN(encode_fd_object)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    struct flat_binder_object* dest = out;
 | 
			
		||||
    struct binder_fd_object* dest = out;
 | 
			
		||||
 | 
			
		||||
    memset(dest, 0, sizeof(*dest));
 | 
			
		||||
    dest->hdr.type = BINDER_TYPE_FD;
 | 
			
		||||
    dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
    dest->handle = fd;
 | 
			
		||||
    dest->pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
    dest->fd = fd;
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -197,7 +240,7 @@ GBINDER_IO_FN(encode_buffer_object)(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_death_notification)(
 | 
			
		||||
GBINDER_IO_FN(encode_handle_cookie)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
@@ -209,7 +252,21 @@ GBINDER_IO_FN(encode_death_notification)(
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes BC_TRANSACTION data */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_ptr_cookie)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    struct binder_ptr_cookie* dest = out;
 | 
			
		||||
 | 
			
		||||
    /* We never send these cookies and don't expect them back */
 | 
			
		||||
    dest->ptr = (uintptr_t)obj;
 | 
			
		||||
    dest->cookie = 0;
 | 
			
		||||
    return sizeof(dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fills binder_transaction_data for BC_TRANSACTION/REPLY */
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
GBINDER_IO_FN(fill_transaction_data)(
 | 
			
		||||
@@ -217,7 +274,7 @@ GBINDER_IO_FN(fill_transaction_data)(
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* payload,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    guint tx_flags,
 | 
			
		||||
    GUtilIntArray* offsets,
 | 
			
		||||
    void** offsets_buf)
 | 
			
		||||
{
 | 
			
		||||
@@ -226,9 +283,7 @@ GBINDER_IO_FN(fill_transaction_data)(
 | 
			
		||||
    tr->code = code;
 | 
			
		||||
    tr->data_size = payload->len;
 | 
			
		||||
    tr->data.ptr.buffer = (uintptr_t)payload->data;
 | 
			
		||||
    if (flags & GBINDER_TX_FLAG_ONEWAY) {
 | 
			
		||||
        tr->flags |= TF_ONE_WAY;
 | 
			
		||||
    }
 | 
			
		||||
    tr->flags = tx_flags;
 | 
			
		||||
    if (offsets && offsets->count) {
 | 
			
		||||
        guint i;
 | 
			
		||||
        binder_size_t* tx_offsets = g_new(binder_size_t, offsets->count);
 | 
			
		||||
@@ -244,6 +299,7 @@ GBINDER_IO_FN(fill_transaction_data)(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes BC_TRANSACTION data */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_transaction)(
 | 
			
		||||
@@ -257,7 +313,8 @@ GBINDER_IO_FN(encode_transaction)(
 | 
			
		||||
{
 | 
			
		||||
    struct binder_transaction_data* tr = out;
 | 
			
		||||
 | 
			
		||||
    GBINDER_IO_FN(fill_transaction_data)(tr, handle, code, payload, flags,
 | 
			
		||||
    GBINDER_IO_FN(fill_transaction_data)(tr, handle, code, payload,
 | 
			
		||||
        (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS,
 | 
			
		||||
        offsets, offsets_buf);
 | 
			
		||||
    return sizeof(*tr);
 | 
			
		||||
}
 | 
			
		||||
@@ -278,13 +335,53 @@ GBINDER_IO_FN(encode_transaction_sg)(
 | 
			
		||||
    struct binder_transaction_data_sg* sg = out;
 | 
			
		||||
 | 
			
		||||
    GBINDER_IO_FN(fill_transaction_data)(&sg->transaction_data, handle, code,
 | 
			
		||||
        payload, flags, offsets, offsets_buf);
 | 
			
		||||
        payload, (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS,
 | 
			
		||||
        offsets, offsets_buf);
 | 
			
		||||
    /* The driver seems to require buffers to be 8-byte aligned */
 | 
			
		||||
    sg->buffers_size = G_ALIGN8(buffers_size);
 | 
			
		||||
    return sizeof(*sg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encode BC_REPLY */
 | 
			
		||||
/* Encodes BC_REPLY data */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_reply)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* payload,
 | 
			
		||||
    GUtilIntArray* offsets,
 | 
			
		||||
    void** offsets_buf)
 | 
			
		||||
{
 | 
			
		||||
    struct binder_transaction_data* tr = out;
 | 
			
		||||
 | 
			
		||||
    GBINDER_IO_FN(fill_transaction_data)(tr, handle, code, payload, 0,
 | 
			
		||||
        offsets, offsets_buf);
 | 
			
		||||
    return sizeof(*tr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes BC_REPLY_SG data */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_reply_sg)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* payload,
 | 
			
		||||
    GUtilIntArray* offsets,
 | 
			
		||||
    void** offsets_buf,
 | 
			
		||||
    gsize buffers_size)
 | 
			
		||||
{
 | 
			
		||||
    struct binder_transaction_data_sg* sg = out;
 | 
			
		||||
 | 
			
		||||
    GBINDER_IO_FN(fill_transaction_data)(&sg->transaction_data, handle, code,
 | 
			
		||||
        payload, 0, offsets, offsets_buf);
 | 
			
		||||
    /* The driver seems to require buffers to be 8-byte aligned */
 | 
			
		||||
    sg->buffers_size = G_ALIGN8(buffers_size);
 | 
			
		||||
    return sizeof(*sg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encode BC_REPLY with just status */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_status_reply)(
 | 
			
		||||
@@ -372,7 +469,7 @@ GBINDER_IO_FN(decode_cookie)(
 | 
			
		||||
/* Decode struct binder_ptr_cookie */
 | 
			
		||||
static
 | 
			
		||||
void*
 | 
			
		||||
GBINDER_IO_FN(decode_binder_ptr_cookie)(
 | 
			
		||||
GBINDER_IO_FN(decode_ptr_cookie)(
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    const struct binder_ptr_cookie* ptr = data;
 | 
			
		||||
@@ -382,6 +479,24 @@ GBINDER_IO_FN(decode_binder_ptr_cookie)(
 | 
			
		||||
    return (void*)(uintptr_t)ptr->ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(decode_binder_handle)(
 | 
			
		||||
    const void* data,
 | 
			
		||||
    guint32* handle)
 | 
			
		||||
{
 | 
			
		||||
    const struct flat_binder_object* obj = data;
 | 
			
		||||
 | 
			
		||||
    /* Caller guarantees that data points to an object */
 | 
			
		||||
    if (obj->hdr.type == BINDER_TYPE_HANDLE) {
 | 
			
		||||
        if (handle) {
 | 
			
		||||
            *handle = obj->handle;
 | 
			
		||||
        }
 | 
			
		||||
        return sizeof(*obj);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(decode_binder_object)(
 | 
			
		||||
@@ -396,9 +511,19 @@ GBINDER_IO_FN(decode_binder_object)(
 | 
			
		||||
        switch (obj->hdr.type) {
 | 
			
		||||
        case BINDER_TYPE_HANDLE:
 | 
			
		||||
            if (out) {
 | 
			
		||||
                *out = gbinder_object_registry_get_remote(reg, obj->handle);
 | 
			
		||||
                *out = gbinder_object_registry_get_remote(reg, obj->handle,
 | 
			
		||||
                    REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE);
 | 
			
		||||
            }
 | 
			
		||||
            return sizeof(*obj);
 | 
			
		||||
        case BINDER_TYPE_BINDER:
 | 
			
		||||
            if (!obj->binder) {
 | 
			
		||||
                /* That's a NULL reference */
 | 
			
		||||
                if (out) {
 | 
			
		||||
                    *out = NULL;
 | 
			
		||||
                }
 | 
			
		||||
                return sizeof(*obj);
 | 
			
		||||
            }
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            GERR("Unsupported binder object type 0x%08x", obj->hdr.type);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -503,23 +628,29 @@ const GBinderIo GBINDER_IO_PREFIX = {
 | 
			
		||||
        .failed_reply = BR_FAILED_REPLY
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    .object_size = GBINDER_IO_FN(object_size),
 | 
			
		||||
    .object_data_size = GBINDER_IO_FN(object_data_size),
 | 
			
		||||
 | 
			
		||||
    /* Encoders */
 | 
			
		||||
    .encode_pointer = GBINDER_IO_FN(encode_pointer),
 | 
			
		||||
    .encode_cookie = GBINDER_IO_FN(encode_cookie),
 | 
			
		||||
    .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_buffer_object = GBINDER_IO_FN(encode_buffer_object),
 | 
			
		||||
    .encode_death_notification = GBINDER_IO_FN(encode_death_notification),
 | 
			
		||||
    .encode_handle_cookie = GBINDER_IO_FN(encode_handle_cookie),
 | 
			
		||||
    .encode_ptr_cookie = GBINDER_IO_FN(encode_ptr_cookie),
 | 
			
		||||
    .encode_transaction = GBINDER_IO_FN(encode_transaction),
 | 
			
		||||
    .encode_transaction_sg = GBINDER_IO_FN(encode_transaction_sg),
 | 
			
		||||
    .encode_reply = GBINDER_IO_FN(encode_reply),
 | 
			
		||||
    .encode_reply_sg = GBINDER_IO_FN(encode_reply_sg),
 | 
			
		||||
    .encode_status_reply = GBINDER_IO_FN(encode_status_reply),
 | 
			
		||||
 | 
			
		||||
    /* Decoders */
 | 
			
		||||
    .decode_transaction_data = GBINDER_IO_FN(decode_transaction_data),
 | 
			
		||||
    .decode_cookie = GBINDER_IO_FN(decode_cookie),
 | 
			
		||||
    .decode_binder_ptr_cookie = GBINDER_IO_FN(decode_binder_ptr_cookie),
 | 
			
		||||
    .decode_ptr_cookie = GBINDER_IO_FN(decode_ptr_cookie),
 | 
			
		||||
    .decode_binder_handle = GBINDER_IO_FN(decode_binder_handle),
 | 
			
		||||
    .decode_binder_object = GBINDER_IO_FN(decode_binder_object),
 | 
			
		||||
    .decode_buffer_object = GBINDER_IO_FN(decode_buffer_object),
 | 
			
		||||
    .decode_fd_object = GBINDER_IO_FN(decode_fd_object),
 | 
			
		||||
@@ -535,7 +666,7 @@ G_STATIC_ASSERT(sizeof(struct flat_binder_object) <=
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_buffer_object) <=
 | 
			
		||||
    GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_handle_cookie) <=
 | 
			
		||||
    GBINDER_MAX_DEATH_NOTIFICATION_SIZE);
 | 
			
		||||
    GBINDER_MAX_HANDLE_COOKIE_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_transaction_data) <=
 | 
			
		||||
    GBINDER_MAX_BC_TRANSACTION_SIZE);
 | 
			
		||||
G_STATIC_ASSERT(sizeof(struct binder_transaction_data_sg) <=
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -125,7 +125,8 @@ struct gbinder_io {
 | 
			
		||||
        guint failed_reply;
 | 
			
		||||
    } br;
 | 
			
		||||
 | 
			
		||||
    /* Size of the object's extra data */
 | 
			
		||||
    /* Size of the object and its extra data */
 | 
			
		||||
    gsize (*object_size)(const void* obj);
 | 
			
		||||
    gsize (*object_data_size)(const void* obj);
 | 
			
		||||
 | 
			
		||||
    /* Writes pointer to the buffer. The destination buffer must have
 | 
			
		||||
@@ -134,6 +135,12 @@ struct gbinder_io {
 | 
			
		||||
#define GBINDER_MAX_POINTER_SIZE (8)
 | 
			
		||||
    guint (*encode_pointer)(void* out, const void* pointer);
 | 
			
		||||
 | 
			
		||||
    /* Writes cookie to the buffer. The destination buffer must have
 | 
			
		||||
     * at least GBINDER_IO_MAX_COOKIE_SIZE bytes available. The
 | 
			
		||||
     * actual size is returned. */
 | 
			
		||||
#define GBINDER_MAX_COOKIE_SIZE GBINDER_MAX_POINTER_SIZE
 | 
			
		||||
    guint (*encode_cookie)(void* out, guint64 cookie);
 | 
			
		||||
 | 
			
		||||
    /* Encode flat_buffer_object */
 | 
			
		||||
#define GBINDER_MAX_BINDER_OBJECT_SIZE (24)
 | 
			
		||||
    guint (*encode_local_object)(void* out, GBinderLocalObject* obj);
 | 
			
		||||
@@ -145,32 +152,42 @@ struct gbinder_io {
 | 
			
		||||
    guint (*encode_buffer_object)(void* out, const void* data, gsize size,
 | 
			
		||||
        const GBinderParent* parent);
 | 
			
		||||
 | 
			
		||||
    /* Encode death notification */
 | 
			
		||||
#define GBINDER_MAX_DEATH_NOTIFICATION_SIZE (12)
 | 
			
		||||
    guint (*encode_death_notification)(void* out, GBinderRemoteObject* obj);
 | 
			
		||||
    /* Encode binder_handle_cookie */
 | 
			
		||||
#define GBINDER_MAX_HANDLE_COOKIE_SIZE (12)
 | 
			
		||||
    guint (*encode_handle_cookie)(void* out, GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
    /* Encode BC_TRANSACTION/REPLY data */
 | 
			
		||||
    /* Encode binder_ptr_cookie */
 | 
			
		||||
#define GBINDER_MAX_PTR_COOKIE_SIZE (16)
 | 
			
		||||
    guint (*encode_ptr_cookie)(void* out, GBinderLocalObject* obj);
 | 
			
		||||
 | 
			
		||||
    /* Encode BC_TRANSACTION/BC_TRANSACTION_SG data */
 | 
			
		||||
#define GBINDER_MAX_BC_TRANSACTION_SIZE (64)
 | 
			
		||||
    guint (*encode_transaction)(void* out, guint32 handle, guint32 code,
 | 
			
		||||
        const GByteArray* data, guint flags /* See below */,
 | 
			
		||||
        GUtilIntArray* offsets, void** offsets_buf);
 | 
			
		||||
 | 
			
		||||
    /* Encode BC_TRANSACTION_SG/REPLY_SG data */
 | 
			
		||||
#define GBINDER_MAX_BC_TRANSACTION_SG_SIZE (72)
 | 
			
		||||
    guint (*encode_transaction_sg)(void* out, guint32 handle, guint32 code,
 | 
			
		||||
        const GByteArray* data, guint flags /* GBINDER_TX_FLAG_xxx */,
 | 
			
		||||
        GUtilIntArray* offsets, void** offsets_buf,
 | 
			
		||||
        gsize buffers_size);
 | 
			
		||||
 | 
			
		||||
    /* Encode BC_REPLY/REPLY_SG data */
 | 
			
		||||
#define GBINDER_MAX_BC_REPLY_SIZE GBINDER_MAX_BC_TRANSACTION_SIZE
 | 
			
		||||
    guint (*encode_reply)(void* out, guint32 handle, guint32 code,
 | 
			
		||||
        const GByteArray* data, GUtilIntArray* offsets, void** offsets_buf);
 | 
			
		||||
#define GBINDER_MAX_BC_REPLY_SG_SIZE GBINDER_MAX_BC_TRANSACTION_SG_SIZE
 | 
			
		||||
    guint (*encode_reply_sg)(void* out, guint32 handle, guint32 code,
 | 
			
		||||
        const GByteArray* data, GUtilIntArray* offsets, void** offsets_buf,
 | 
			
		||||
        gsize buffers_size);
 | 
			
		||||
 | 
			
		||||
    /* Encode BC_REPLY */
 | 
			
		||||
    guint (*encode_status_reply)(void* out, gint32* status);
 | 
			
		||||
 | 
			
		||||
    /* Decoders */
 | 
			
		||||
    void (*decode_transaction_data)(const void* data, GBinderIoTxData* tx);
 | 
			
		||||
 | 
			
		||||
#define GBINDER_MAX_PTR_COOKIE_SIZE (16)
 | 
			
		||||
    void* (*decode_binder_ptr_cookie)(const void* data);
 | 
			
		||||
    void* (*decode_ptr_cookie)(const void* data);
 | 
			
		||||
    guint (*decode_cookie)(const void* data, guint64* cookie);
 | 
			
		||||
    guint (*decode_binder_handle)(const void* obj, guint32* handle);
 | 
			
		||||
    guint (*decode_binder_object)(const void* data, gsize size,
 | 
			
		||||
        GBinderObjectRegistry* reg, GBinderRemoteObject** obj);
 | 
			
		||||
    guint (*decode_buffer_object)(GBinderBuffer* buf, gsize offset,
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -31,11 +31,13 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define GLIB_DISABLE_DEPRECATION_WARNINGS
 | 
			
		||||
#define _GNU_SOURCE  /* pthread_*_np */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_handler.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
@@ -53,6 +55,7 @@
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_ipc_looper GBinderIpcLooper;
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +64,7 @@ struct gbinder_ipc_priv {
 | 
			
		||||
    GThreadPool* tx_pool;
 | 
			
		||||
    GHashTable* tx_table;
 | 
			
		||||
    char* key;
 | 
			
		||||
    const char* name;
 | 
			
		||||
    GBinderObjectRegistry object_registry;
 | 
			
		||||
 | 
			
		||||
    GMutex remote_objects_mutex;
 | 
			
		||||
@@ -90,6 +94,7 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
 | 
			
		||||
#define GBINDER_IPC_MAX_TX_THREADS (15)
 | 
			
		||||
#define GBINDER_IPC_MAX_PRIMARY_LOOPERS (5)
 | 
			
		||||
#define GBINDER_IPC_LOOPER_START_TIMEOUT_SEC (2)
 | 
			
		||||
#define GBINDER_IPC_LOOPER_JOIN_TIMEOUT_MS (500)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * When looper receives the transaction:
 | 
			
		||||
@@ -153,15 +158,21 @@ struct gbinder_ipc_looper {
 | 
			
		||||
    GBinderHandler handler;
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderIpc* ipc; /* Not a reference! */
 | 
			
		||||
    GThread* thread;
 | 
			
		||||
    pthread_t thread;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    GCond start_cond;
 | 
			
		||||
    gint exit;
 | 
			
		||||
    gint started;
 | 
			
		||||
    gint joined;
 | 
			
		||||
    int pipefd[2];
 | 
			
		||||
    int txfd[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_ipc_tx_handler {
 | 
			
		||||
    int pipefd[2];
 | 
			
		||||
    int txfd[2];
 | 
			
		||||
} GBinderIpcTxHandler;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_ipc_tx_priv GBinderIpcTxPriv;
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
@@ -197,13 +208,64 @@ typedef struct gbinder_ipc_tx_custom {
 | 
			
		||||
} GBinderIpcTxCustom;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
 | 
			
		||||
    { return gbinder_driver_dev(self->driver); }
 | 
			
		||||
    { return self->priv->name; }
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderIpcLooper*
 | 
			
		||||
gbinder_ipc_looper_new(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req);
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Utilities
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_wait(
 | 
			
		||||
    int fd_wakeup,
 | 
			
		||||
    int fd_read,
 | 
			
		||||
    guint8* out)
 | 
			
		||||
{
 | 
			
		||||
    struct pollfd fds[2];
 | 
			
		||||
 | 
			
		||||
    memset(fds, 0, sizeof(fds));
 | 
			
		||||
    fds[0].fd = fd_wakeup;
 | 
			
		||||
    fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
    fds[1].fd = fd_read;
 | 
			
		||||
    fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
    if (poll(fds, 2, -1) < 0) {
 | 
			
		||||
        GWARN("Transaction pipe polling error: %s", strerror(errno));
 | 
			
		||||
    } else if (fds[1].revents & POLLIN) {
 | 
			
		||||
        const ssize_t n = read(fds[1].fd, out, 1);
 | 
			
		||||
 | 
			
		||||
        if (n == 1) {
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else if (n < 0) {
 | 
			
		||||
            GWARN("Transaction pipe read error: %s", strerror(errno));
 | 
			
		||||
        } else {
 | 
			
		||||
            GWARN("Nothing was read from the transaction pipe");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcLooperTx
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -367,8 +429,8 @@ void
 | 
			
		||||
gbinder_ipc_looper_free(
 | 
			
		||||
    GBinderIpcLooper* looper)
 | 
			
		||||
{
 | 
			
		||||
    if (looper->thread) {
 | 
			
		||||
        g_thread_unref(looper->thread);
 | 
			
		||||
    if (!looper->joined) {
 | 
			
		||||
        pthread_join(looper->thread, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    close(looper->pipefd[0]);
 | 
			
		||||
    close(looper->pipefd[1]);
 | 
			
		||||
@@ -566,6 +628,16 @@ gbinder_ipc_looper_count_primary(
 | 
			
		||||
    return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_looper_can_loop(
 | 
			
		||||
    GBinderHandler* handler)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler);
 | 
			
		||||
 | 
			
		||||
    return !g_atomic_int_get(&looper->exit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_ipc_looper_transact(
 | 
			
		||||
@@ -589,7 +661,6 @@ gbinder_ipc_looper_transact(
 | 
			
		||||
        GBinderIpcLooperTx* tx = gbinder_ipc_looper_tx_new(obj, code, flags,
 | 
			
		||||
            req, looper->txfd);
 | 
			
		||||
        GBinderIpcPriv* priv = ipc->priv;
 | 
			
		||||
        struct pollfd fds[2];
 | 
			
		||||
        guint8 done = 0;
 | 
			
		||||
        gboolean was_blocked = FALSE;
 | 
			
		||||
        /* Let GBinderLocalObject handle the transaction on the main thread */
 | 
			
		||||
@@ -598,69 +669,52 @@ gbinder_ipc_looper_transact(
 | 
			
		||||
                gbinder_ipc_looper_tx_ref(tx), gbinder_ipc_looper_tx_done);
 | 
			
		||||
 | 
			
		||||
        /* Wait for either transaction completion or looper shutdown */
 | 
			
		||||
        memset(fds, 0, sizeof(fds));
 | 
			
		||||
        fds[0].fd = looper->pipefd[0];
 | 
			
		||||
        fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
        fds[1].fd = tx->pipefd[0];
 | 
			
		||||
        fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
        poll(fds, 2, -1);
 | 
			
		||||
        if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done) &&
 | 
			
		||||
            done == TX_BLOCKED) {
 | 
			
		||||
            /*
 | 
			
		||||
             * We are going to block this looper for potentially
 | 
			
		||||
             * significant period of time. Start new looper to
 | 
			
		||||
             * accept normal incoming requests and terminate this
 | 
			
		||||
             * one when we are done with this transaction.
 | 
			
		||||
             *
 | 
			
		||||
             * For the duration of the transaction, this looper is
 | 
			
		||||
             * moved to the blocked_loopers list.
 | 
			
		||||
             */
 | 
			
		||||
            GBinderIpcPriv* priv = looper->ipc->priv;
 | 
			
		||||
            GBinderIpcLooper* new_looper = NULL;
 | 
			
		||||
 | 
			
		||||
        if ((fds[1].revents & POLLIN) &&
 | 
			
		||||
            read(fds[1].fd, &done, sizeof(done)) == 1) {
 | 
			
		||||
            /* Normal completion */
 | 
			
		||||
            if (done == TX_BLOCKED) {
 | 
			
		||||
                /*
 | 
			
		||||
                 * We are going to block this looper for potentially
 | 
			
		||||
                 * significant period of time. Start new looper to
 | 
			
		||||
                 * accept normal incoming requests and terminate this
 | 
			
		||||
                 * one when we are done with this transaction.
 | 
			
		||||
                 *
 | 
			
		||||
                 * For the duration of the transaction, this looper is
 | 
			
		||||
                 * moved to the blocked_loopers list.
 | 
			
		||||
                 */
 | 
			
		||||
                GBinderIpcPriv* priv = looper->ipc->priv;
 | 
			
		||||
                GBinderIpcLooper* new_looper = NULL;
 | 
			
		||||
            /* Lock */
 | 
			
		||||
            g_mutex_lock(&priv->looper_mutex);
 | 
			
		||||
            if (gbinder_ipc_looper_remove_primary(looper)) {
 | 
			
		||||
                GVERBOSE("Primary looper %s is blocked", looper->name);
 | 
			
		||||
                looper->next = priv->blocked_loopers;
 | 
			
		||||
                priv->blocked_loopers = looper;
 | 
			
		||||
                was_blocked = TRUE;
 | 
			
		||||
 | 
			
		||||
                /* Lock */
 | 
			
		||||
                g_mutex_lock(&priv->looper_mutex);
 | 
			
		||||
                if (gbinder_ipc_looper_remove_primary(looper)) {
 | 
			
		||||
                    GDEBUG("Primary looper %s is blocked", looper->name);
 | 
			
		||||
                    looper->next = priv->blocked_loopers;
 | 
			
		||||
                    priv->blocked_loopers = looper;
 | 
			
		||||
                    was_blocked = TRUE;
 | 
			
		||||
 | 
			
		||||
                    /* If there's no more primary loopers left, create one */
 | 
			
		||||
                    if (!priv->primary_loopers) {
 | 
			
		||||
                        new_looper = gbinder_ipc_looper_new(ipc);
 | 
			
		||||
                        if (new_looper) {
 | 
			
		||||
                            /* Will unref it after it gets started */
 | 
			
		||||
                            gbinder_ipc_looper_ref(new_looper);
 | 
			
		||||
                            priv->primary_loopers = new_looper;
 | 
			
		||||
                        }
 | 
			
		||||
                /* If there's no more primary loopers left, create one */
 | 
			
		||||
                if (!priv->primary_loopers) {
 | 
			
		||||
                    new_looper = gbinder_ipc_looper_new(ipc);
 | 
			
		||||
                    if (new_looper) {
 | 
			
		||||
                        /* Will unref it after it gets started */
 | 
			
		||||
                        gbinder_ipc_looper_ref(new_looper);
 | 
			
		||||
                        priv->primary_loopers = new_looper;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                g_mutex_unlock(&priv->looper_mutex);
 | 
			
		||||
                /* Unlock */
 | 
			
		||||
            }
 | 
			
		||||
            g_mutex_unlock(&priv->looper_mutex);
 | 
			
		||||
            /* Unlock */
 | 
			
		||||
 | 
			
		||||
                if (new_looper) {
 | 
			
		||||
                    /* Wait until it gets started */
 | 
			
		||||
                    gbinder_ipc_looper_start(new_looper);
 | 
			
		||||
                    gbinder_ipc_looper_unref(new_looper);
 | 
			
		||||
                }
 | 
			
		||||
            if (new_looper) {
 | 
			
		||||
                /* Wait until it gets started */
 | 
			
		||||
                gbinder_ipc_looper_start(new_looper);
 | 
			
		||||
                gbinder_ipc_looper_unref(new_looper);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                /* Block until asynchronous transaction gets completed. */
 | 
			
		||||
                done = 0;
 | 
			
		||||
                memset(fds, 0, sizeof(fds));
 | 
			
		||||
                fds[0].fd = looper->pipefd[0];
 | 
			
		||||
                fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
                fds[1].fd = tx->pipefd[0];
 | 
			
		||||
                fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
                poll(fds, 2, -1);
 | 
			
		||||
                if ((fds[1].revents & POLLIN) &&
 | 
			
		||||
                    read(fds[1].fd, &done, sizeof(done)) == 1) {
 | 
			
		||||
                    GDEBUG("Looper %s is released", looper->name);
 | 
			
		||||
                    GASSERT(done == TX_DONE);
 | 
			
		||||
                }
 | 
			
		||||
            /* Block until asynchronous transaction gets completed. */
 | 
			
		||||
            done = 0;
 | 
			
		||||
            if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done)) {
 | 
			
		||||
                GVERBOSE("Looper %s is released", looper->name);
 | 
			
		||||
                GASSERT(done == TX_DONE);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -668,17 +722,16 @@ gbinder_ipc_looper_transact(
 | 
			
		||||
            GASSERT(done == TX_DONE);
 | 
			
		||||
            reply = gbinder_local_reply_ref(tx->reply);
 | 
			
		||||
            status = tx->status;
 | 
			
		||||
            if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) {
 | 
			
		||||
                /*
 | 
			
		||||
                 * This wasn't the last references meaning that
 | 
			
		||||
                 * gbinder_ipc_looper_tx_free() will close the
 | 
			
		||||
                 * descriptors and we will have to create a new
 | 
			
		||||
                 * pipe for the next transaction.
 | 
			
		||||
                 */
 | 
			
		||||
                looper->txfd[0] = looper->txfd[1] = -1;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_ipc_looper_tx_unref(tx, FALSE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) {
 | 
			
		||||
            /*
 | 
			
		||||
             * This wasn't the last reference meaning that
 | 
			
		||||
             * gbinder_ipc_looper_tx_free() will close the
 | 
			
		||||
             * descriptors and we will have to create a new
 | 
			
		||||
             * pipe for the next transaction.
 | 
			
		||||
             */
 | 
			
		||||
            looper->txfd[0] = looper->txfd[1] = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        gbinder_idle_callback_destroy(callback);
 | 
			
		||||
@@ -713,6 +766,7 @@ gbinder_ipc_looper_thread(
 | 
			
		||||
    GBinderIpcLooper* looper = data;
 | 
			
		||||
    GBinderDriver* driver = looper->driver;
 | 
			
		||||
 | 
			
		||||
    pthread_setname_np(looper->thread, looper->name);
 | 
			
		||||
    if (gbinder_driver_enter_looper(driver)) {
 | 
			
		||||
        struct pollfd pipefd;
 | 
			
		||||
        int res;
 | 
			
		||||
@@ -803,9 +857,9 @@ gbinder_ipc_looper_new(
 | 
			
		||||
    /* Note: this call can actually fail */
 | 
			
		||||
    if (!pipe(fd)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_functions = {
 | 
			
		||||
            .can_loop = gbinder_ipc_looper_can_loop,
 | 
			
		||||
            .transact = gbinder_ipc_looper_transact
 | 
			
		||||
        };
 | 
			
		||||
        GError* error = NULL;
 | 
			
		||||
        GBinderIpcLooper* looper = g_slice_new0(GBinderIpcLooper);
 | 
			
		||||
        static gint gbinder_ipc_next_looper_id = 1;
 | 
			
		||||
        guint id = (guint)g_atomic_int_add(&gbinder_ipc_next_looper_id, 1);
 | 
			
		||||
@@ -819,16 +873,14 @@ gbinder_ipc_looper_new(
 | 
			
		||||
        looper->handler.f = &handler_functions;
 | 
			
		||||
        looper->ipc = ipc;
 | 
			
		||||
        looper->driver = gbinder_driver_ref(ipc->driver);
 | 
			
		||||
        looper->thread = g_thread_try_new(looper->name,
 | 
			
		||||
            gbinder_ipc_looper_thread, looper, &error);
 | 
			
		||||
        if (looper->thread) {
 | 
			
		||||
        if (!pthread_create(&looper->thread, NULL, gbinder_ipc_looper_thread,
 | 
			
		||||
            looper)) {
 | 
			
		||||
            /* gbinder_ipc_looper_thread() will release this reference: */
 | 
			
		||||
            gbinder_ipc_looper_ref(looper);
 | 
			
		||||
            GDEBUG("Starting looper %s", looper->name);
 | 
			
		||||
            return looper;
 | 
			
		||||
        } else {
 | 
			
		||||
            GERR("Failed to create looper thread: %s", GERRMSG(error));
 | 
			
		||||
            g_error_free(error);
 | 
			
		||||
            GERR("Failed to create looper thread %s", looper->name);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_ipc_looper_unref(looper);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -875,13 +927,15 @@ gbinder_ipc_looper_stop(
 | 
			
		||||
    GBinderIpcLooper* looper)
 | 
			
		||||
{
 | 
			
		||||
    /* Caller checks looper for NULL */
 | 
			
		||||
    if (looper->thread && looper->thread != g_thread_self()) {
 | 
			
		||||
        guint8 done = TX_DONE;
 | 
			
		||||
 | 
			
		||||
    if (looper->thread) {
 | 
			
		||||
        GDEBUG("Stopping looper %s", looper->name);
 | 
			
		||||
        g_atomic_int_set(&looper->exit, TRUE);
 | 
			
		||||
        if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
 | 
			
		||||
            looper->thread = NULL;
 | 
			
		||||
        if (looper->thread != pthread_self()) {
 | 
			
		||||
            guint8 done = TX_DONE;
 | 
			
		||||
 | 
			
		||||
            if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
 | 
			
		||||
                GWARN("Failed to stop looper %s", looper->name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -910,13 +964,151 @@ gbinder_ipc_looper_join(
 | 
			
		||||
    GBinderIpcLooper* looper)
 | 
			
		||||
{
 | 
			
		||||
    /* Caller checks looper for NULL */
 | 
			
		||||
    if (looper->thread && looper->thread != g_thread_self()) {
 | 
			
		||||
        g_thread_join(looper->thread);
 | 
			
		||||
        looper->thread = NULL;
 | 
			
		||||
    if (looper->thread && looper->thread != pthread_self()) {
 | 
			
		||||
        struct timespec ts;
 | 
			
		||||
        int err = clock_gettime(CLOCK_REALTIME, &ts);
 | 
			
		||||
 | 
			
		||||
        if (!err) {
 | 
			
		||||
            const long ms = 1000000;
 | 
			
		||||
            const long sec = 1000 * ms;
 | 
			
		||||
            const long ns = ts.tv_nsec + GBINDER_IPC_LOOPER_JOIN_TIMEOUT_MS*ms;
 | 
			
		||||
 | 
			
		||||
            ts.tv_sec += ns / sec;
 | 
			
		||||
            ts.tv_nsec = ns % sec;
 | 
			
		||||
            err = pthread_timedjoin_np(looper->thread, NULL, &ts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (err) {
 | 
			
		||||
            /* Assume that looper is stuck in read */
 | 
			
		||||
            GBinderIpc* ipc = looper->ipc;
 | 
			
		||||
            GBinderIpcPriv* priv = ipc->priv;
 | 
			
		||||
 | 
			
		||||
            GDEBUG("Looper %s is stuck", looper->name);
 | 
			
		||||
 | 
			
		||||
            /* Lock */
 | 
			
		||||
            g_mutex_lock(&priv->looper_mutex);
 | 
			
		||||
            gbinder_driver_close(ipc->driver);
 | 
			
		||||
            g_mutex_unlock(&priv->looper_mutex);
 | 
			
		||||
            /* Unlock */
 | 
			
		||||
 | 
			
		||||
            pthread_join(looper->thread, NULL);
 | 
			
		||||
        }
 | 
			
		||||
        looper->joined = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    looper->ipc = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcTxHandler
 | 
			
		||||
 *
 | 
			
		||||
 * It's needed to handle the following scenario:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Asynchronous call is made. The actual transaction is performed on
 | 
			
		||||
 *    gbinder_ipc_tx_proc thread.
 | 
			
		||||
 * 2. While we were are waiting for completion of our transaction, we
 | 
			
		||||
 *    receive a valid incoming transation.
 | 
			
		||||
 * 3. This transaction is handled by gbinder_ipc_tx_handler_transact.
 | 
			
		||||
 *
 | 
			
		||||
 * This seems to be quite a rare scenario, so we allocate a new
 | 
			
		||||
 * GBinderIpcTxHandler (and new pipes) for each such transaction,
 | 
			
		||||
 * to keep things as simple as possible.
 | 
			
		||||
 *
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderIpcTxHandler*
 | 
			
		||||
gbinder_ipc_tx_handler_new(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcTxHandler* h = g_slice_new0(GBinderIpcTxHandler);
 | 
			
		||||
 | 
			
		||||
    /* Note: pipe() calls can actually fail */
 | 
			
		||||
    if (!pipe(h->txfd)) {
 | 
			
		||||
        if (!pipe(h->pipefd)) {
 | 
			
		||||
            return h;
 | 
			
		||||
        } else {
 | 
			
		||||
            GERR("Failed to create a tx pipe: %s", strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
        close(h->txfd[0]);
 | 
			
		||||
        close(h->txfd[1]);
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("Failed to create a tx pipe: %s", strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
    g_slice_free(GBinderIpcTxHandler, h);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_tx_handler_free(
 | 
			
		||||
    GBinderIpcTxHandler* h)
 | 
			
		||||
{
 | 
			
		||||
    close(h->pipefd[0]);
 | 
			
		||||
    close(h->pipefd[1]);
 | 
			
		||||
    if (h->txfd[0] >= 0) {
 | 
			
		||||
        close(h->txfd[0]);
 | 
			
		||||
        close(h->txfd[1]);
 | 
			
		||||
    }
 | 
			
		||||
    g_slice_free(GBinderIpcTxHandler, h);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_ipc_tx_handler_transact(
 | 
			
		||||
    GBinderHandler* handler,
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* result)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcTxHandler* h = gbinder_ipc_tx_handler_new();
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    int status = -EFAULT;
 | 
			
		||||
 | 
			
		||||
    if (h) {
 | 
			
		||||
        GBinderIpcLooperTx* tx = gbinder_ipc_looper_tx_new(obj, code, flags,
 | 
			
		||||
            req, h->txfd);
 | 
			
		||||
        guint8 done = 0;
 | 
			
		||||
        /* Handle transaction on the main thread */
 | 
			
		||||
        GBinderEventLoopCallback* callback =
 | 
			
		||||
            gbinder_idle_callback_schedule_new(gbinder_ipc_looper_tx_handle,
 | 
			
		||||
                gbinder_ipc_looper_tx_ref(tx), gbinder_ipc_looper_tx_done);
 | 
			
		||||
 | 
			
		||||
        /* Wait for completion */
 | 
			
		||||
        if (gbinder_ipc_wait(h->pipefd[0], tx->pipefd[0], &done) &&
 | 
			
		||||
            done == TX_BLOCKED) {
 | 
			
		||||
            /* Block until asynchronous transaction gets completed. */
 | 
			
		||||
            done = 0;
 | 
			
		||||
            if (gbinder_ipc_wait(h->pipefd[0], tx->pipefd[0], &done)) {
 | 
			
		||||
                GASSERT(done == TX_DONE);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (done) {
 | 
			
		||||
            GASSERT(done == TX_DONE);
 | 
			
		||||
            reply = gbinder_local_reply_ref(tx->reply);
 | 
			
		||||
            status = tx->status;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) {
 | 
			
		||||
            /*
 | 
			
		||||
             * This wasn't the last references meaning that
 | 
			
		||||
             * gbinder_ipc_looper_tx_free() will close the
 | 
			
		||||
             * descriptors and we will have to create a new
 | 
			
		||||
             * pipe for the next transaction.
 | 
			
		||||
             */
 | 
			
		||||
            h->txfd[0] = h->txfd[1] = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        gbinder_idle_callback_destroy(callback);
 | 
			
		||||
        gbinder_ipc_tx_handler_free(h);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *result = status;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderObjectRegistry
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -924,16 +1116,24 @@ gbinder_ipc_looper_join(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_remote_handle_locked(
 | 
			
		||||
    GBinderIpcPriv* priv,
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    /* Caller holds priv->remote_objects_mutex */
 | 
			
		||||
    if (priv->remote_objects) {
 | 
			
		||||
        GVERBOSE_("handle %u", handle);
 | 
			
		||||
        g_hash_table_remove(priv->remote_objects, GINT_TO_POINTER(handle));
 | 
			
		||||
        if (g_hash_table_size(priv->remote_objects) == 0) {
 | 
			
		||||
            g_hash_table_unref(priv->remote_objects);
 | 
			
		||||
            priv->remote_objects = NULL;
 | 
			
		||||
        const gpointer key = GINT_TO_POINTER(handle);
 | 
			
		||||
#if GUTIL_LOG_VERBOSE
 | 
			
		||||
        const gpointer obj = g_hash_table_lookup(priv->remote_objects, key);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        if (g_hash_table_remove(priv->remote_objects, key)) {
 | 
			
		||||
            GVERBOSE_("handle %u %p %s", handle, obj, gbinder_ipc_name(self));
 | 
			
		||||
            if (g_hash_table_size(priv->remote_objects) == 0) {
 | 
			
		||||
                g_hash_table_unref(priv->remote_objects);
 | 
			
		||||
                priv->remote_objects = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -947,7 +1147,7 @@ gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->remote_objects_mutex);
 | 
			
		||||
    gbinder_ipc_invalidate_remote_handle_locked(priv, handle);
 | 
			
		||||
    gbinder_ipc_invalidate_remote_handle_locked(self, handle);
 | 
			
		||||
    g_mutex_unlock(&priv->remote_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
}
 | 
			
		||||
@@ -982,10 +1182,12 @@ gbinder_ipc_local_object_disposed(
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->local_objects_mutex);
 | 
			
		||||
    if (obj->object.ref_count == 1 && priv->local_objects) {
 | 
			
		||||
        g_hash_table_remove(priv->local_objects, obj);
 | 
			
		||||
        if (g_hash_table_size(priv->local_objects) == 0) {
 | 
			
		||||
            g_hash_table_unref(priv->local_objects);
 | 
			
		||||
            priv->local_objects = NULL;
 | 
			
		||||
        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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
@@ -1002,7 +1204,7 @@ gbinder_ipc_remote_object_disposed(
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    g_mutex_lock(&priv->remote_objects_mutex);
 | 
			
		||||
    if (obj->object.ref_count == 1) {
 | 
			
		||||
        gbinder_ipc_invalidate_remote_handle_locked(priv, obj->handle);
 | 
			
		||||
        gbinder_ipc_invalidate_remote_handle_locked(self, obj->handle);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->remote_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
@@ -1020,11 +1222,13 @@ gbinder_ipc_register_local_object(
 | 
			
		||||
    if (!priv->local_objects) {
 | 
			
		||||
        priv->local_objects = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
    }
 | 
			
		||||
    g_hash_table_insert(priv->local_objects, obj, obj);
 | 
			
		||||
    if (!g_hash_table_contains(priv->local_objects, obj)) {
 | 
			
		||||
        g_hash_table_insert(priv->local_objects, obj, obj);
 | 
			
		||||
        GVERBOSE_("%p %s", obj, gbinder_ipc_name(self));
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
    /* Unlock */
 | 
			
		||||
 | 
			
		||||
    GVERBOSE_("%p", obj);
 | 
			
		||||
    gbinder_ipc_looper_check(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1061,6 +1265,7 @@ GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_priv_get_remote_object(
 | 
			
		||||
    GBinderIpcPriv* priv,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    REMOTE_REGISTRY_CREATE create,
 | 
			
		||||
    gboolean maybe_dead)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
@@ -1073,16 +1278,22 @@ gbinder_ipc_priv_get_remote_object(
 | 
			
		||||
    }
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        gbinder_remote_object_ref(obj);
 | 
			
		||||
    } else {
 | 
			
		||||
    } else if (create != REMOTE_REGISTRY_DONT_CREATE) {
 | 
			
		||||
        GBinderIpc* self = priv->self;
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * If maybe_dead is TRUE, the caller is supposed to try reanimating
 | 
			
		||||
         * the object on the main thread not holding any global locks.
 | 
			
		||||
         */
 | 
			
		||||
        obj = gbinder_remote_object_new(priv->self, handle, maybe_dead);
 | 
			
		||||
        obj = gbinder_remote_object_new(self, handle, maybe_dead ?
 | 
			
		||||
            REMOTE_OBJECT_CREATE_DEAD : (create == REMOTE_REGISTRY_CAN_CREATE) ?
 | 
			
		||||
            REMOTE_OBJECT_CREATE_ALIVE :
 | 
			
		||||
            REMOTE_OBJECT_CREATE_ACQUIRED);
 | 
			
		||||
        if (!priv->remote_objects) {
 | 
			
		||||
            priv->remote_objects = g_hash_table_new
 | 
			
		||||
                (g_direct_hash, g_direct_equal);
 | 
			
		||||
        }
 | 
			
		||||
        GVERBOSE_("%p handle %u %s", obj, handle, gbinder_ipc_name(self));
 | 
			
		||||
        g_hash_table_replace(priv->remote_objects, key, obj);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&priv->remote_objects_mutex);
 | 
			
		||||
@@ -1091,14 +1302,47 @@ gbinder_ipc_priv_get_remote_object(
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_get_remote_object(
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_ipc_find_local_object(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean maybe_dead)
 | 
			
		||||
    GBinderIpcLocalObjectCheckFunc func,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* found = NULL;
 | 
			
		||||
 | 
			
		||||
    if (self)  {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        /* Lock */
 | 
			
		||||
        g_mutex_lock(&priv->local_objects_mutex);
 | 
			
		||||
        if (priv->local_objects) {
 | 
			
		||||
            GHashTableIter it;
 | 
			
		||||
            gpointer value;
 | 
			
		||||
 | 
			
		||||
            g_hash_table_iter_init(&it, priv->local_objects);
 | 
			
		||||
            while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
                GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(value);
 | 
			
		||||
 | 
			
		||||
                if (func(obj, user_data)) {
 | 
			
		||||
                    found = gbinder_local_object_ref(obj);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        g_mutex_unlock(&priv->local_objects_mutex);
 | 
			
		||||
        /* Unlock */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_get_service_manager(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
{
 | 
			
		||||
    /* GBinderServiceManager makes sure that GBinderIpc pointer is not NULL */
 | 
			
		||||
    return gbinder_ipc_priv_get_remote_object(self->priv, handle, maybe_dead);
 | 
			
		||||
    return gbinder_ipc_priv_get_remote_object(self->priv,
 | 
			
		||||
        GBINDER_SERVICEMANAGER_HANDLE, REMOTE_REGISTRY_CAN_CREATE, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
@@ -1147,10 +1391,11 @@ static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_object_registry_get_remote(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    REMOTE_REGISTRY_CREATE create)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_ipc_priv_get_remote_object
 | 
			
		||||
        (gbinder_ipc_priv_from_object_registry(reg), handle, FALSE);
 | 
			
		||||
        (gbinder_ipc_priv_from_object_registry(reg), handle, create, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -1289,24 +1534,14 @@ gbinder_ipc_tx_internal_exec(
 | 
			
		||||
    GBinderIpcTxPriv* priv)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv);
 | 
			
		||||
    GBinderIpcTx* pub = &priv->pub;
 | 
			
		||||
    GBinderIpc* self = pub->ipc;
 | 
			
		||||
    GBinderObjectRegistry* reg = &self->priv->object_registry;
 | 
			
		||||
    GBinderIpc* ipc = priv->pub.ipc;
 | 
			
		||||
 | 
			
		||||
    /* Perform synchronous transaction */
 | 
			
		||||
    if (tx->flags & GBINDER_TX_FLAG_ONEWAY) {
 | 
			
		||||
        tx->status = gbinder_driver_transact(self->driver, reg, tx->handle,
 | 
			
		||||
            tx->code, tx->req, NULL);
 | 
			
		||||
        tx->status = gbinder_ipc_transact_sync_oneway_worker(ipc, tx->handle,
 | 
			
		||||
            tx->code, tx->req);
 | 
			
		||||
    } else {
 | 
			
		||||
        tx->reply = gbinder_remote_reply_new(&self->priv->object_registry);
 | 
			
		||||
        tx->status = gbinder_driver_transact(self->driver, reg, tx->handle,
 | 
			
		||||
            tx->code, tx->req, tx->reply);
 | 
			
		||||
        if (tx->status != GBINDER_STATUS_OK &&
 | 
			
		||||
            gbinder_remote_reply_is_empty(tx->reply)) {
 | 
			
		||||
            /* Drop useless reply */
 | 
			
		||||
            gbinder_remote_reply_unref(tx->reply);
 | 
			
		||||
            tx->reply = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        tx->reply = gbinder_ipc_transact_sync_reply_worker(ipc, tx->handle,
 | 
			
		||||
            tx->code, tx->req, &tx->status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1429,18 +1664,142 @@ gbinder_ipc_tx_proc(
 | 
			
		||||
    gbinder_idle_callback_schedule(tx->completion);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcSyncApi for worker threads
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    /* Must be invoked on worker thread */
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_fn = {
 | 
			
		||||
            .can_loop = NULL,
 | 
			
		||||
            .transact = gbinder_ipc_tx_handler_transact
 | 
			
		||||
        };
 | 
			
		||||
        GBinderHandler handler = { &handler_fn };
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, &handler,
 | 
			
		||||
            handle, code, req, reply);
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    /* Must be invoked on worker thread */
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_fn = {
 | 
			
		||||
            .can_loop = NULL,
 | 
			
		||||
            .transact = gbinder_ipc_tx_handler_transact
 | 
			
		||||
        };
 | 
			
		||||
        GBinderHandler handler = { &handler_fn };
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            &handler, handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GBinderIpcSyncApi gbinder_ipc_sync_worker = {
 | 
			
		||||
    .sync_reply = gbinder_ipc_transact_sync_reply_worker,
 | 
			
		||||
    .sync_oneway = gbinder_ipc_transact_sync_oneway_worker
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcSyncApi for the main thread
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
 | 
			
		||||
            code, req, reply);
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            NULL, handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GBinderIpcSyncApi gbinder_ipc_sync_main = {
 | 
			
		||||
    .sync_reply = gbinder_ipc_transact_sync_reply,
 | 
			
		||||
    .sync_oneway = gbinder_ipc_transact_sync_oneway
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_ipc_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    const GBinderRpcProtocol* protocol)
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* self = NULL;
 | 
			
		||||
    const GBinderRpcProtocol* protocol;
 | 
			
		||||
 | 
			
		||||
    if (!dev || !dev[0]) dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    protocol = gbinder_rpc_protocol_for_device(dev); /* Never returns NULL */
 | 
			
		||||
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    pthread_mutex_lock(&gbinder_ipc_mutex);
 | 
			
		||||
    if (gbinder_ipc_table) {
 | 
			
		||||
@@ -1464,6 +1823,9 @@ gbinder_ipc_new(
 | 
			
		||||
                gbinder_ipc_table = g_hash_table_new(g_str_hash, g_str_equal);
 | 
			
		||||
            }
 | 
			
		||||
            g_hash_table_replace(gbinder_ipc_table, priv->key, self);
 | 
			
		||||
            /* With "/dev/" prefix, it may be too long to be a thread name */
 | 
			
		||||
            priv->name = priv->key +
 | 
			
		||||
                (g_str_has_prefix(priv->key, "/dev/") ? 5 : 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pthread_mutex_unlock(&gbinder_ipc_mutex);
 | 
			
		||||
@@ -1496,52 +1858,37 @@ GBinderObjectRegistry*
 | 
			
		||||
gbinder_ipc_object_registry(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
{
 | 
			
		||||
    /* Only used by unit tests */
 | 
			
		||||
    return G_LIKELY(self) ? &self->priv->object_registry : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
const GBinderIo*
 | 
			
		||||
gbinder_ipc_io(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, handle, code,
 | 
			
		||||
            req, reply);
 | 
			
		||||
    return G_LIKELY(self) ? gbinder_driver_io(self->driver) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_ipc_protocol(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? gbinder_driver_protocol(self->driver) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
gbinder_ipc_ping_sync(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver);
 | 
			
		||||
    guint32 code = gbinder_driver_protocol(driver)->ping_tx;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_remote_reply_unref(api->sync_reply(ipc, handle, code, req, &ret));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
@@ -1611,6 +1958,14 @@ gbinder_ipc_cancel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_set_max_threads(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    gint max)
 | 
			
		||||
{
 | 
			
		||||
    return g_thread_pool_set_max_threads(self->priv->tx_pool, max, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -1682,7 +2037,11 @@ gbinder_ipc_dispose(
 | 
			
		||||
    GVERBOSE_("%s", self->dev);
 | 
			
		||||
    /* Lock */
 | 
			
		||||
    pthread_mutex_lock(&gbinder_ipc_mutex);
 | 
			
		||||
    GASSERT(gbinder_ipc_table);
 | 
			
		||||
    /*
 | 
			
		||||
     * gbinder_ipc_dispose() can be invoked more than once (typically
 | 
			
		||||
     * at shutdown) and gbinder_ipc_table here may actually happen to
 | 
			
		||||
     * be NULL, hence the check.
 | 
			
		||||
     */
 | 
			
		||||
    if (gbinder_ipc_table) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -59,6 +59,12 @@ struct gbinder_ipc_tx {
 | 
			
		||||
    void* user_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
gboolean
 | 
			
		||||
(*GBinderIpcLocalObjectCheckFunc)(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*GBinderIpcReplyFunc)(
 | 
			
		||||
@@ -67,10 +73,34 @@ void
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
(*GBinderIpcSyncReplyFunc)(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
int
 | 
			
		||||
(*GBinderIpcSyncOnewayFunc)(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req);
 | 
			
		||||
 | 
			
		||||
struct gbinder_ipc_sync_api {
 | 
			
		||||
    GBinderIpcSyncReplyFunc sync_reply;
 | 
			
		||||
    GBinderIpcSyncOnewayFunc sync_oneway;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const GBinderIpcSyncApi gbinder_ipc_sync_main GBINDER_INTERNAL;
 | 
			
		||||
extern const GBinderIpcSyncApi gbinder_ipc_sync_worker GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_ipc_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    const GBinderRpcProtocol* protocol)
 | 
			
		||||
    const char* dev)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
@@ -85,7 +115,7 @@ gbinder_ipc_unref(
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_looper_check(
 | 
			
		||||
   GBinderIpc* ipc)
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderObjectRegistry*
 | 
			
		||||
@@ -93,6 +123,24 @@ gbinder_ipc_object_registry(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
const GBinderIo*
 | 
			
		||||
gbinder_ipc_io(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_ipc_protocol(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_ipc_find_local_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderIpcLocalObjectCheckFunc func,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_register_local_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -100,11 +148,10 @@ gbinder_ipc_register_local_object(
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_get_remote_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean maybe_dead)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
gbinder_ipc_get_service_manager(
 | 
			
		||||
    GBinderIpc* self)
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
@@ -112,21 +159,11 @@ gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
gbinder_ipc_ping_sync(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
@@ -170,12 +207,19 @@ gbinder_ipc_remote_object_disposed(
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Needed by unit tests */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_set_max_threads(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    gint max_threads)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Declared for unit tests */
 | 
			
		||||
__attribute__((destructor))
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_exit(
 | 
			
		||||
    void)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    GBINDER_DESTRUCTOR;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_IPC_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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -34,6 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply_p.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
@@ -41,6 +42,7 @@
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
@@ -51,8 +53,14 @@ struct gbinder_local_object_priv {
 | 
			
		||||
    void* user_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_local_object_acquire_data {
 | 
			
		||||
    GBinderLocalObject* object;
 | 
			
		||||
    GBinderBufferContentsList* bufs;
 | 
			
		||||
} GBinderLocalObjectAcquireData;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderLocalObject, gbinder_local_object, G_TYPE_OBJECT)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_local_object_parent_class
 | 
			
		||||
#define GBINDER_LOCAL_OBJECT_GET_CLASS(obj) \
 | 
			
		||||
    G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_LOCAL_OBJECT, \
 | 
			
		||||
    GBinderLocalObjectClass)
 | 
			
		||||
@@ -256,6 +264,18 @@ gbinder_local_object_default_handle_looper_transaction(
 | 
			
		||||
    return handler(self, req, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_default_drop(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    /* Clear the transaction callback */
 | 
			
		||||
    priv->txproc = NULL;
 | 
			
		||||
    priv->user_data = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_handle_later(
 | 
			
		||||
@@ -272,10 +292,10 @@ gbinder_local_object_handle_later(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_local_object_handle_increfs_proc(
 | 
			
		||||
    gpointer local)
 | 
			
		||||
gbinder_local_object_increfs_proc(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data);
 | 
			
		||||
 | 
			
		||||
    self->weak_refs++;
 | 
			
		||||
    g_signal_emit(self, gbinder_local_object_signals
 | 
			
		||||
@@ -285,10 +305,10 @@ gbinder_local_object_handle_increfs_proc(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_local_object_handle_decrefs_proc(
 | 
			
		||||
    gpointer local)
 | 
			
		||||
gbinder_local_object_decrefs_proc(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data);
 | 
			
		||||
 | 
			
		||||
    GASSERT(self->weak_refs > 0);
 | 
			
		||||
    self->weak_refs--;
 | 
			
		||||
@@ -298,30 +318,66 @@ gbinder_local_object_handle_decrefs_proc(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_local_object_handle_acquire_proc(
 | 
			
		||||
    gpointer local)
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_default_acquire(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
 | 
			
		||||
    self->strong_refs++;
 | 
			
		||||
    gbinder_local_object_ref(self);
 | 
			
		||||
    GVERBOSE_("%p => %d", self, self->strong_refs);
 | 
			
		||||
    g_signal_emit(self, gbinder_local_object_signals
 | 
			
		||||
        [SIGNAL_STRONG_REFS_CHANGED], 0);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_local_object_handle_release_proc(
 | 
			
		||||
    gpointer local)
 | 
			
		||||
gbinder_local_object_acquire_proc(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
    GBinderLocalObjectAcquireData* data = user_data;
 | 
			
		||||
    GBinderLocalObject* self = data->object;
 | 
			
		||||
 | 
			
		||||
    GASSERT(self->strong_refs > 0);
 | 
			
		||||
    self->strong_refs--;
 | 
			
		||||
    g_signal_emit(self, gbinder_local_object_signals
 | 
			
		||||
        [SIGNAL_STRONG_REFS_CHANGED], 0);
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_GET_CLASS(self)->acquire(self);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_acquire_done(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObjectAcquireData* data = user_data;
 | 
			
		||||
    GBinderLocalObject* self = data->object;
 | 
			
		||||
 | 
			
		||||
    gbinder_driver_acquire_done(self->ipc->driver, self);
 | 
			
		||||
    gbinder_local_object_unref(self);
 | 
			
		||||
    gbinder_buffer_contents_list_free(data->bufs);
 | 
			
		||||
    return gutil_slice_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_default_release(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GASSERT(self->strong_refs > 0);
 | 
			
		||||
    if (self->strong_refs > 0) {
 | 
			
		||||
        self->strong_refs--;
 | 
			
		||||
        GVERBOSE_("%p => %d", self, self->strong_refs);
 | 
			
		||||
        g_signal_emit(self, gbinder_local_object_signals
 | 
			
		||||
            [SIGNAL_STRONG_REFS_CHANGED], 0);
 | 
			
		||||
        gbinder_local_object_unref(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_local_object_release_proc(
 | 
			
		||||
    gpointer obj)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(obj);
 | 
			
		||||
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_GET_CLASS(self)->release(self);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -335,42 +391,65 @@ gbinder_local_object_new(
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data) /* Since 1.0.30 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_local_object_new_with_type(GBINDER_TYPE_LOCAL_OBJECT,
 | 
			
		||||
        ipc, ifaces, txproc, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_new_with_type(
 | 
			
		||||
    GType type,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* arg)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(ipc)) {
 | 
			
		||||
        GBinderLocalObject* self = g_object_new
 | 
			
		||||
            (GBINDER_TYPE_LOCAL_OBJECT, NULL);
 | 
			
		||||
        GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
        guint i = 0, n = gutil_strv_length((char**)ifaces);
 | 
			
		||||
        gboolean append_base_interface;
 | 
			
		||||
        GBinderLocalObject* obj = g_object_new(type, NULL);
 | 
			
		||||
 | 
			
		||||
        if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
 | 
			
		||||
            append_base_interface = TRUE;
 | 
			
		||||
            n++;
 | 
			
		||||
        } else {
 | 
			
		||||
            append_base_interface = FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        priv->ifaces = g_new(char*, n + 1);
 | 
			
		||||
        if (ifaces) {
 | 
			
		||||
            while (*ifaces) {
 | 
			
		||||
                priv->ifaces[i++] = g_strdup(*ifaces++);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (append_base_interface) {
 | 
			
		||||
            priv->ifaces[i++] = g_strdup(hidl_base_interface);
 | 
			
		||||
        }
 | 
			
		||||
        priv->ifaces[i] = NULL;
 | 
			
		||||
 | 
			
		||||
        self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
        self->ifaces = (const char**)priv->ifaces;
 | 
			
		||||
        priv->txproc = txproc;
 | 
			
		||||
        priv->user_data = user_data;
 | 
			
		||||
        gbinder_ipc_register_local_object(ipc, self);
 | 
			
		||||
        return self;
 | 
			
		||||
        gbinder_local_object_init_base(obj, ipc, ifaces, txproc, arg);
 | 
			
		||||
        gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
        return obj;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_init_base(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
    guint i = 0, n = gutil_strv_length((char**)ifaces);
 | 
			
		||||
    gboolean append_base_interface;
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
 | 
			
		||||
        append_base_interface = TRUE;
 | 
			
		||||
        n++;
 | 
			
		||||
    } else {
 | 
			
		||||
        append_base_interface = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    priv->ifaces = g_new(char*, n + 1);
 | 
			
		||||
    if (ifaces) {
 | 
			
		||||
        while (*ifaces) {
 | 
			
		||||
            priv->ifaces[i++] = g_strdup(*ifaces++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (append_base_interface) {
 | 
			
		||||
        priv->ifaces[i++] = g_strdup(hidl_base_interface);
 | 
			
		||||
    }
 | 
			
		||||
    priv->ifaces[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
    self->ifaces = (const char**)priv->ifaces;
 | 
			
		||||
    priv->txproc = txproc;
 | 
			
		||||
    priv->user_data = user_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_ref(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
@@ -397,11 +476,7 @@ gbinder_local_object_drop(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        /* Clear the transaction callback */
 | 
			
		||||
        priv->txproc = NULL;
 | 
			
		||||
        priv->user_data = NULL;
 | 
			
		||||
        GBINDER_LOCAL_OBJECT_GET_CLASS(self)->drop(self);
 | 
			
		||||
        g_object_unref(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -495,33 +570,51 @@ void
 | 
			
		||||
gbinder_local_object_handle_increfs(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_handle_later(self,
 | 
			
		||||
        gbinder_local_object_handle_increfs_proc);
 | 
			
		||||
    gbinder_local_object_handle_later(self, gbinder_local_object_increfs_proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_handle_decrefs(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_handle_later(self,
 | 
			
		||||
        gbinder_local_object_handle_decrefs_proc);
 | 
			
		||||
    gbinder_local_object_handle_later(self, gbinder_local_object_decrefs_proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_handle_acquire(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderBufferContentsList* bufs)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_ref(self);
 | 
			
		||||
    gbinder_local_object_handle_later(self,
 | 
			
		||||
        gbinder_local_object_handle_acquire_proc);
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
        GBinderLocalObjectAcquireData* data =
 | 
			
		||||
            g_slice_new(GBinderLocalObjectAcquireData);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * This is a bit complicated :)
 | 
			
		||||
         * GBinderProxyObject derived from GBinderLocalObject acquires a
 | 
			
		||||
         * reference to the remote object in addition to performing the
 | 
			
		||||
         * default GBinderLocalObject action later on the main thread.
 | 
			
		||||
         * We must ensure that remote object doesn't go away before we
 | 
			
		||||
         * acquire our reference to it. One of the references to that
 | 
			
		||||
         * remote object (possibly the last one) may be associated with
 | 
			
		||||
         * the transaction buffer contained in GBinderBufferContentsList.
 | 
			
		||||
         * We don't know exactly which one we need, so we keep all those
 | 
			
		||||
         * buffers alive until we are done with BR_ACQUIRE.
 | 
			
		||||
         */
 | 
			
		||||
        data->object = gbinder_local_object_ref(self);
 | 
			
		||||
        data->bufs = gbinder_buffer_contents_list_dup(bufs);
 | 
			
		||||
        g_main_context_invoke_full(priv->context, G_PRIORITY_DEFAULT,
 | 
			
		||||
            gbinder_local_object_acquire_proc, data,
 | 
			
		||||
            gbinder_local_object_acquire_done);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_handle_release(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_handle_later(self,
 | 
			
		||||
        gbinder_local_object_handle_release_proc);
 | 
			
		||||
    gbinder_local_object_handle_later(self, gbinder_local_object_release_proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -543,26 +636,26 @@ gbinder_local_object_init(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_dispose(
 | 
			
		||||
    GObject* local)
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(object);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_local_object_disposed(self->ipc, self);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_local_object_parent_class)->dispose(local);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->dispose(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_finalize(
 | 
			
		||||
    GObject* local)
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
 | 
			
		||||
    GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(object);
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!self->strong_refs);
 | 
			
		||||
    gbinder_ipc_unref(self->ipc);
 | 
			
		||||
    g_strfreev(priv->ifaces);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_local_object_parent_class)->finalize(local);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -582,6 +675,9 @@ gbinder_local_object_class_init(
 | 
			
		||||
        gbinder_local_object_default_handle_looper_transaction;
 | 
			
		||||
    klass->can_handle_transaction =
 | 
			
		||||
        gbinder_local_object_default_can_handle_transaction;
 | 
			
		||||
    klass->acquire = gbinder_local_object_default_acquire;
 | 
			
		||||
    klass->release = gbinder_local_object_default_release;
 | 
			
		||||
    klass->drop = gbinder_local_object_default_drop;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_signals[SIGNAL_WEAK_REFS_CHANGED] =
 | 
			
		||||
        g_signal_new(SIGNAL_WEAK_REFS_CHANGED_NAME,
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -40,8 +40,9 @@
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Some if this stuff may become public if we decide to allow the clients
 | 
			
		||||
 * to derive their classes from GBinderLocalObject
 | 
			
		||||
 * Some of this stuff may become public if we decide to allow the clients
 | 
			
		||||
 * to derive their own classes from GBinderLocalObject. For now it's all
 | 
			
		||||
 * private.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
@@ -76,6 +77,9 @@ typedef struct gbinder_local_object_class {
 | 
			
		||||
    GBinderLocalReply* (*handle_looper_transaction)
 | 
			
		||||
        (GBinderLocalObject* self, GBinderRemoteRequest* req, guint code,
 | 
			
		||||
            guint flags, int* status);
 | 
			
		||||
    void (*acquire)(GBinderLocalObject* self);
 | 
			
		||||
    void (*release)(GBinderLocalObject* self);
 | 
			
		||||
    void (*drop)(GBinderLocalObject* self);
 | 
			
		||||
    /* Need to add some placeholders if this class becomes public */
 | 
			
		||||
} GBinderLocalObjectClass;
 | 
			
		||||
 | 
			
		||||
@@ -83,10 +87,30 @@ GType gbinder_local_object_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
#define GBINDER_TYPE_LOCAL_OBJECT (gbinder_local_object_get_type())
 | 
			
		||||
#define GBINDER_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObject))
 | 
			
		||||
#define GBINDER_LOCAL_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectClass)
 | 
			
		||||
 | 
			
		||||
#define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
 | 
			
		||||
#define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_new_with_type(
 | 
			
		||||
    GType type,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_init_base(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_local_object_add_weak_refs_changed_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
@@ -144,7 +168,8 @@ gbinder_local_object_handle_decrefs(
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_handle_acquire(
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderBufferContentsList* bufs)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -43,6 +43,7 @@ struct gbinder_local_reply {
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    GBinderWriterData data;
 | 
			
		||||
    GBinderOutputData out;
 | 
			
		||||
    GBinderBufferContents* contents;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
@@ -94,14 +95,16 @@ gbinder_local_reply_new(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
gbinder_local_reply_set_contents(
 | 
			
		||||
    GBinderLocalReply* self,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = gbinder_buffer_io(buffer);
 | 
			
		||||
    GBinderLocalReply* self = gbinder_local_reply_new(io);
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer);
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer, convert);
 | 
			
		||||
        gbinder_buffer_contents_unref(self->contents);
 | 
			
		||||
        self->contents = gbinder_buffer_contents_ref
 | 
			
		||||
            (gbinder_buffer_contents(buffer));
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
@@ -116,7 +119,8 @@ gbinder_local_reply_free(
 | 
			
		||||
    gutil_int_array_free(data->offsets, TRUE);
 | 
			
		||||
    g_byte_array_free(data->bytes, TRUE);
 | 
			
		||||
    gbinder_cleanup_free(data->cleanup);
 | 
			
		||||
    g_slice_free(GBinderLocalReply, self);
 | 
			
		||||
    gbinder_buffer_contents_unref(self->contents);
 | 
			
		||||
    gutil_slice_free(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
@@ -149,6 +153,13 @@ gbinder_local_reply_data(
 | 
			
		||||
    return G_LIKELY(self) ? &self->out :  NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_local_reply_contents(
 | 
			
		||||
    GBinderLocalReply* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->contents :  NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_reply_cleanup(
 | 
			
		||||
    GBinderLocalReply* self,
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -47,9 +47,16 @@ gbinder_local_reply_data(
 | 
			
		||||
    GBinderLocalReply* reply)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_local_reply_contents(
 | 
			
		||||
    GBinderLocalReply* reply)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
gbinder_local_reply_set_contents(
 | 
			
		||||
    GBinderLocalReply* reply,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_LOCAL_REPLY_PRIVATE_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
#include "gbinder_writer_p.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
@@ -91,6 +92,7 @@ gbinder_local_request_new(
 | 
			
		||||
        if (init) {
 | 
			
		||||
            gsize size;
 | 
			
		||||
            gconstpointer data = g_bytes_get_data(init, &size);
 | 
			
		||||
 | 
			
		||||
            writer->bytes = g_byte_array_sized_new(size);
 | 
			
		||||
            g_byte_array_append(writer->bytes, data, size);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -103,19 +105,49 @@ gbinder_local_request_new(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_iface(
 | 
			
		||||
    const GBinderIo* io,
 | 
			
		||||
    const GBinderRpcProtocol* protocol,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* self = gbinder_local_request_new(io, NULL);
 | 
			
		||||
 | 
			
		||||
    if (self && G_LIKELY(protocol) && G_LIKELY(iface)) {
 | 
			
		||||
        GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
        gbinder_local_request_init_writer(self, &writer);
 | 
			
		||||
        protocol->write_rpc_header(&writer, iface);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* self = gbinder_local_request_new
 | 
			
		||||
        (gbinder_buffer_io(buffer), NULL);
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer);
 | 
			
		||||
        gbinder_writer_data_append_contents(&self->data, buffer, 0, convert);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_request_append_contents(
 | 
			
		||||
    GBinderLocalRequest* self,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    gsize off,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    if (self) {
 | 
			
		||||
        gbinder_writer_data_append_contents(&self->data, buffer, off, convert);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_request_free(
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -43,14 +43,30 @@ gbinder_local_request_new(
 | 
			
		||||
    GBytes* init)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_iface(
 | 
			
		||||
    const GBinderIo* io,
 | 
			
		||||
    const GBinderRpcProtocol* protocol,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderOutputData*
 | 
			
		||||
gbinder_local_request_data(
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_request_append_contents(
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_LOCAL_REQUEST_PRIVATE_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								src/gbinder_object_converter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/gbinder_object_converter.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_OBJECT_CONVERTER_H
 | 
			
		||||
#define GBINDER_OBJECT_CONVERTER_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_object_converter_functions {
 | 
			
		||||
    GBinderLocalObject* (*handle_to_local)(GBinderObjectConverter*, guint32);
 | 
			
		||||
} GBinderObjectConverterFunctions;
 | 
			
		||||
 | 
			
		||||
struct gbinder_object_converter {
 | 
			
		||||
    const GBinderObjectConverterFunctions* f;
 | 
			
		||||
    const GBinderIo* io;
 | 
			
		||||
    const GBinderRpcProtocol* protocol;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Inline wrappers */
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_object_converter_handle_to_local(
 | 
			
		||||
    GBinderObjectConverter* convert,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
{
 | 
			
		||||
    return convert ? convert->f->handle_to_local(convert, handle) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_OBJECT_CONVERTER_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,13 +35,19 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
typedef enum gbinder_remote_registry_create {
 | 
			
		||||
    REMOTE_REGISTRY_DONT_CREATE,
 | 
			
		||||
    REMOTE_REGISTRY_CAN_CREATE,
 | 
			
		||||
    REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE
 | 
			
		||||
} REMOTE_REGISTRY_CREATE;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_object_registry_functions {
 | 
			
		||||
    void (*ref)(GBinderObjectRegistry* reg);
 | 
			
		||||
    void (*unref)(GBinderObjectRegistry* reg);
 | 
			
		||||
    GBinderLocalObject* (*get_local)(GBinderObjectRegistry* reg,
 | 
			
		||||
        void* pointer);
 | 
			
		||||
    GBinderRemoteObject* (*get_remote)(GBinderObjectRegistry* reg,
 | 
			
		||||
        guint32 handle);
 | 
			
		||||
        guint32 handle, REMOTE_REGISTRY_CREATE create);
 | 
			
		||||
} GBinderObjectRegistryFunctions;
 | 
			
		||||
 | 
			
		||||
struct gbinder_object_registry {
 | 
			
		||||
@@ -81,9 +87,10 @@ GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_object_registry_get_remote(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    REMOTE_REGISTRY_CREATE create)
 | 
			
		||||
{
 | 
			
		||||
    return reg ? reg->f->get_remote(reg, handle) : NULL;
 | 
			
		||||
    return reg ? reg->f->get_remote(reg, handle, create) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_OBJECT_REGISTRY_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										522
									
								
								src/gbinder_proxy_object.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										522
									
								
								src/gbinder_proxy_object.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,522 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define GLIB_DISABLE_DEPRECATION_WARNINGS
 | 
			
		||||
 | 
			
		||||
#include "gbinder_proxy_object.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_remote_request_p.h"
 | 
			
		||||
#include "gbinder_remote_reply_p.h"
 | 
			
		||||
#include "gbinder_object_converter.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass GBinderProxyObjectClass;
 | 
			
		||||
typedef struct gbinder_proxy_tx GBinderProxyTx;
 | 
			
		||||
 | 
			
		||||
struct gbinder_proxy_tx {
 | 
			
		||||
    GBinderProxyTx* next;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
    gulong id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gbinder_proxy_object_priv {
 | 
			
		||||
    gulong remote_death_id;
 | 
			
		||||
    gboolean dropped;
 | 
			
		||||
    GBinderProxyTx* tx;
 | 
			
		||||
    GMutex mutex; /* Protects the hashtable below */
 | 
			
		||||
    GHashTable* subproxies;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \
 | 
			
		||||
    GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
#define GBINDER_IS_PROXY_OBJECT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \
 | 
			
		||||
    GBINDER_TYPE_PROXY_OBJECT)
 | 
			
		||||
 | 
			
		||||
#define THIS(obj) GBINDER_PROXY_OBJECT(obj)
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderProxyObjectConverter*
 | 
			
		||||
gbinder_proxy_object_converter_cast(
 | 
			
		||||
    GBinderObjectConverter* pub)
 | 
			
		||||
{
 | 
			
		||||
    return G_CAST(pub, GBinderProxyObjectConverter, pub);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_proxy_object_converter_check(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    void* remote)
 | 
			
		||||
{
 | 
			
		||||
    if (GBINDER_IS_PROXY_OBJECT(obj) && THIS(obj)->remote == remote) {
 | 
			
		||||
        /* Found matching proxy object */
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    /* Keep looking */
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_proxy_object_converter_handle_to_local(
 | 
			
		||||
    GBinderObjectConverter* pub,
 | 
			
		||||
    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 */);
 | 
			
		||||
    GBinderLocalObject* local = gbinder_ipc_find_local_object(c->local,
 | 
			
		||||
        gbinder_proxy_object_converter_check, remote);
 | 
			
		||||
 | 
			
		||||
    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 */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Release the reference returned by gbinder_object_registry_get_remote */
 | 
			
		||||
    gbinder_remote_object_unref(remote);
 | 
			
		||||
    return local;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_converter_init(
 | 
			
		||||
    GBinderProxyObjectConverter* convert,
 | 
			
		||||
    GBinderProxyObject* proxy,
 | 
			
		||||
    GBinderIpc* remote,
 | 
			
		||||
    GBinderIpc* local)
 | 
			
		||||
{
 | 
			
		||||
    static const GBinderObjectConverterFunctions gbinder_converter_fn = {
 | 
			
		||||
        .handle_to_local = gbinder_proxy_object_converter_handle_to_local
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GBinderObjectConverter* pub = &convert->pub;
 | 
			
		||||
    GBinderIpc* dest = proxy->parent.ipc;
 | 
			
		||||
 | 
			
		||||
    memset(convert, 0, sizeof(*convert));
 | 
			
		||||
    convert->proxy = proxy;
 | 
			
		||||
    convert->remote = remote;
 | 
			
		||||
    convert->local = local;
 | 
			
		||||
    pub->f = &gbinder_converter_fn;
 | 
			
		||||
    pub->io = gbinder_ipc_io(dest);
 | 
			
		||||
    pub->protocol = gbinder_ipc_protocol(dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_tx_dequeue(
 | 
			
		||||
    GBinderProxyTx* tx)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* proxy = tx->proxy;
 | 
			
		||||
 | 
			
		||||
    if (proxy) {
 | 
			
		||||
        GBinderProxyObjectPriv* priv = proxy->priv;
 | 
			
		||||
 | 
			
		||||
        if (priv->tx) {
 | 
			
		||||
            if (priv->tx == tx) {
 | 
			
		||||
                priv->tx = tx->next;
 | 
			
		||||
            } else {
 | 
			
		||||
                GBinderProxyTx* prev = priv->tx;
 | 
			
		||||
 | 
			
		||||
                while (prev->next) {
 | 
			
		||||
                    if (prev->next == tx) {
 | 
			
		||||
                        prev->next = tx->next;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    prev = prev->next;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        tx->next = NULL;
 | 
			
		||||
        tx->proxy = NULL;
 | 
			
		||||
        g_object_unref(proxy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_tx_reply(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyTx* tx = user_data;
 | 
			
		||||
    GBinderProxyObject* self = tx->proxy;
 | 
			
		||||
    GBinderProxyObjectConverter convert;
 | 
			
		||||
    GBinderLocalReply* fwd;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * For proxy objects auto-created by the reply, the remote side (the
 | 
			
		||||
     * one sent the reply) will be the remote GBinderIpc and this object's
 | 
			
		||||
     * GBinderIpc will be the local, i.e. those proxies will work in the
 | 
			
		||||
     * same direction as the top level object. The direction gets inverted
 | 
			
		||||
     * twice.
 | 
			
		||||
     */
 | 
			
		||||
    gbinder_proxy_object_converter_init(&convert, self, ipc, self->parent.ipc);
 | 
			
		||||
    fwd = gbinder_remote_reply_convert_to_local(reply, &convert.pub);
 | 
			
		||||
    tx->id = 0;
 | 
			
		||||
    gbinder_proxy_tx_dequeue(tx);
 | 
			
		||||
    gbinder_remote_request_complete(tx->req, fwd,
 | 
			
		||||
        (status > 0) ? (-EFAULT) : status);
 | 
			
		||||
    if (status == GBINDER_STATUS_DEAD_OBJECT) {
 | 
			
		||||
        /*
 | 
			
		||||
         * Some kernels sometimes don't bother sending us death notifications.
 | 
			
		||||
         * Let's also interpret BR_DEAD_REPLY as an obituary to make sure that
 | 
			
		||||
         * we don't keep dead remote objects around.
 | 
			
		||||
         */
 | 
			
		||||
        gbinder_remote_object_commit_suicide(self->remote);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_local_reply_unref(fwd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_tx_destroy(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyTx* tx = user_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_proxy_tx_dequeue(tx);
 | 
			
		||||
    gbinder_remote_request_unref(tx->req);
 | 
			
		||||
    gutil_slice_free(tx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_proxy_object_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
    if (!priv->dropped && !gbinder_remote_object_is_dead(remote)) {
 | 
			
		||||
        GBinderLocalRequest* fwd;
 | 
			
		||||
        GBinderProxyTx* tx = g_slice_new0(GBinderProxyTx);
 | 
			
		||||
        GBinderProxyObjectConverter convert;
 | 
			
		||||
 | 
			
		||||
        g_object_ref(tx->proxy = self);
 | 
			
		||||
        tx->req = gbinder_remote_request_ref(req);
 | 
			
		||||
        tx->next = priv->tx;
 | 
			
		||||
        priv->tx = tx;
 | 
			
		||||
 | 
			
		||||
        /* Mark the incoming request as pending */
 | 
			
		||||
        gbinder_remote_request_block(req);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * For auto-created proxy objects, this object's GBinderIpc will
 | 
			
		||||
         * become a remote, and the remote's GBinderIpc will become local
 | 
			
		||||
         * because they work in the opposite direction.
 | 
			
		||||
         */
 | 
			
		||||
        gbinder_proxy_object_converter_init(&convert, self, object->ipc,
 | 
			
		||||
            remote->ipc);
 | 
			
		||||
 | 
			
		||||
        /* Forward the transaction */
 | 
			
		||||
        fwd = gbinder_remote_request_convert_to_local(req, &convert.pub);
 | 
			
		||||
        tx->id = gbinder_ipc_transact(remote->ipc, remote->handle, code, flags,
 | 
			
		||||
            fwd, gbinder_proxy_tx_reply, gbinder_proxy_tx_destroy, tx);
 | 
			
		||||
        gbinder_local_request_unref(fwd);
 | 
			
		||||
        *status = GBINDER_STATUS_OK;
 | 
			
		||||
    } else {
 | 
			
		||||
        *status = (-EBADMSG);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
gbinder_proxy_object_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    /* Process all transactions on the main thread */
 | 
			
		||||
    return GBINDER_LOCAL_TRANSACTION_SUPPORTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_acquire(
 | 
			
		||||
    GBinderLocalObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    if (priv->remote_death_id && !object->strong_refs) {
 | 
			
		||||
        GBinderRemoteObject* remote = self->remote;
 | 
			
		||||
 | 
			
		||||
        /* First strong ref, acquire the attached remote object */
 | 
			
		||||
        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(
 | 
			
		||||
    GBinderLocalObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    priv->dropped = TRUE;
 | 
			
		||||
    gbinder_proxy_object_drop_subproxies(self);
 | 
			
		||||
    GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->drop(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderProxyObject*
 | 
			
		||||
gbinder_proxy_object_new(
 | 
			
		||||
    GBinderIpc* src,
 | 
			
		||||
    GBinderRemoteObject* remote)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(remote)) {
 | 
			
		||||
        /*
 | 
			
		||||
         * We don't need to specify the interface list because all
 | 
			
		||||
         * transactions (including HIDL_GET_DESCRIPTOR_TRANSACTION
 | 
			
		||||
         * and HIDL_DESCRIPTOR_CHAIN_TRANSACTION) are getting forwared
 | 
			
		||||
         * to the remote object.
 | 
			
		||||
         */
 | 
			
		||||
        GBinderLocalObject* object = gbinder_local_object_new_with_type
 | 
			
		||||
            (THIS_TYPE, src, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
        if (object) {
 | 
			
		||||
            GBinderProxyObject* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
            self->remote = gbinder_remote_object_ref(remote);
 | 
			
		||||
            return self;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObject* self = THIS(object);
 | 
			
		||||
    GBinderProxyObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_init(
 | 
			
		||||
    GBinderProxyObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderProxyObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
 | 
			
		||||
        THIS_TYPE, GBinderProxyObjectPriv);
 | 
			
		||||
 | 
			
		||||
    self->priv = priv;
 | 
			
		||||
    g_mutex_init(&priv->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_proxy_object_class_init(
 | 
			
		||||
    GBinderProxyObjectClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object_class = G_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    g_type_class_add_private(klass, sizeof(GBinderProxyObjectPriv));
 | 
			
		||||
    object_class->finalize = gbinder_proxy_object_finalize;
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										66
									
								
								src/gbinder_proxy_object.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/gbinder_proxy_object.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_PROXY_OBJECT_H
 | 
			
		||||
#define GBINDER_PROXY_OBJECT_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_proxy_object_priv GBinderProxyObjectPriv;
 | 
			
		||||
 | 
			
		||||
struct gbinder_proxy_object {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GBinderProxyObjectPriv* priv;
 | 
			
		||||
    GBinderRemoteObject* remote;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GType gbinder_proxy_object_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
#define GBINDER_TYPE_PROXY_OBJECT gbinder_proxy_object_get_type()
 | 
			
		||||
#define GBINDER_PROXY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_PROXY_OBJECT, GBinderProxyObject))
 | 
			
		||||
 | 
			
		||||
/* Registers with src and forwards all transactions to the remote */
 | 
			
		||||
GBinderProxyObject*
 | 
			
		||||
gbinder_proxy_object_new(
 | 
			
		||||
    GBinderIpc* src,
 | 
			
		||||
    GBinderRemoteObject* remote)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_PROXY_OBJECT_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -40,15 +40,16 @@
 | 
			
		||||
 | 
			
		||||
struct gbinder_remote_object_priv {
 | 
			
		||||
    GMainContext* context;
 | 
			
		||||
    gboolean acquired;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef GObjectClass GBinderRemoteObjectClass;
 | 
			
		||||
GType gbinder_remote_object_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
G_DEFINE_TYPE(GBinderRemoteObject, gbinder_remote_object, G_TYPE_OBJECT)
 | 
			
		||||
 | 
			
		||||
GType gbinder_remote_object_get_type(void);
 | 
			
		||||
#define GBINDER_TYPE_REMOTE_OBJECT (gbinder_remote_object_get_type())
 | 
			
		||||
#define GBINDER_REMOTE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_REMOTE_OBJECT, GBinderRemoteObject))
 | 
			
		||||
#define PARENT_CLASS gbinder_remote_object_parent_class
 | 
			
		||||
#define THIS_TYPE (gbinder_remote_object_get_type())
 | 
			
		||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,THIS_TYPE,GBinderRemoteObject)
 | 
			
		||||
 | 
			
		||||
enum gbinder_remote_object_signal {
 | 
			
		||||
    SIGNAL_DEATH,
 | 
			
		||||
@@ -65,31 +66,31 @@ static guint gbinder_remote_object_signals[SIGNAL_COUNT] = { 0 };
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_died_on_main_thread(
 | 
			
		||||
gbinder_remote_object_handle_death_on_main_thread(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = self->ipc;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!self->dead);
 | 
			
		||||
    if (!self->dead) {
 | 
			
		||||
        GBinderIpc* ipc = self->ipc;
 | 
			
		||||
        GBinderDriver* driver = ipc->driver;
 | 
			
		||||
        GBinderRemoteObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        self->dead = TRUE;
 | 
			
		||||
        priv->acquired = FALSE;
 | 
			
		||||
        /* ServiceManager always has the same handle, and can be reanimated. */
 | 
			
		||||
        if (self->handle != GBINDER_SERVICEMANAGER_HANDLE) {
 | 
			
		||||
            gbinder_ipc_invalidate_remote_handle(self->ipc, self->handle);
 | 
			
		||||
            gbinder_ipc_invalidate_remote_handle(ipc, self->handle);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_driver_clear_death_notification(driver, self);
 | 
			
		||||
        gbinder_driver_release(driver, self->handle);
 | 
			
		||||
        gbinder_driver_dead_binder_done(driver, self);
 | 
			
		||||
        g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_object_died_handle(
 | 
			
		||||
gbinder_remote_object_death_notification_proc(
 | 
			
		||||
    gpointer self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_remote_object_died_on_main_thread(GBINDER_REMOTE_OBJECT(self));
 | 
			
		||||
    gbinder_remote_object_handle_death_on_main_thread(THIS(self));
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -108,15 +109,20 @@ gbinder_remote_object_reanimate(
 | 
			
		||||
     */
 | 
			
		||||
    if (self->dead) {
 | 
			
		||||
        GBinderIpc* ipc = self->ipc;
 | 
			
		||||
        GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
        guint32 handle = self->handle;
 | 
			
		||||
 | 
			
		||||
        /* Kick the horse */
 | 
			
		||||
        GASSERT(self->handle == GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
        if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
 | 
			
		||||
        if (gbinder_ipc_ping_sync(ipc, handle, &gbinder_ipc_sync_main) == 0) {
 | 
			
		||||
            GBinderRemoteObjectPriv* priv = self->priv;
 | 
			
		||||
            GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
            /* Wow, it's alive! */
 | 
			
		||||
            self->dead = FALSE;
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, self->handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
            priv->acquired = TRUE;
 | 
			
		||||
            gbinder_ipc_looper_check(ipc); /* For death notifications */
 | 
			
		||||
            gbinder_driver_acquire(driver, handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(driver, self);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return !self->dead;
 | 
			
		||||
@@ -130,8 +136,26 @@ gbinder_remote_object_handle_death_notification(
 | 
			
		||||
     * checked the object pointer */
 | 
			
		||||
    GVERBOSE_("%p %u", self, self->handle);
 | 
			
		||||
    g_main_context_invoke_full(self->priv->context, G_PRIORITY_DEFAULT,
 | 
			
		||||
        gbinder_remote_object_died_handle, gbinder_remote_object_ref(self),
 | 
			
		||||
        g_object_unref);
 | 
			
		||||
        gbinder_remote_object_death_notification_proc,
 | 
			
		||||
        gbinder_remote_object_ref(self), g_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_commit_suicide(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    /* This function is only invoked by GBinderProxyObject in context of
 | 
			
		||||
     * the main thread, the object pointer is checked by the caller */
 | 
			
		||||
    if (!self->dead) {
 | 
			
		||||
        GBinderRemoteObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        self->dead = TRUE;
 | 
			
		||||
        priv->acquired = FALSE;
 | 
			
		||||
        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 */
 | 
			
		||||
        g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -142,16 +166,29 @@ GBinderRemoteObject*
 | 
			
		||||
gbinder_remote_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean dead)
 | 
			
		||||
    REMOTE_OBJECT_CREATE create)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(ipc)) {
 | 
			
		||||
        GBinderRemoteObject* self = g_object_new
 | 
			
		||||
            (GBINDER_TYPE_REMOTE_OBJECT, NULL);
 | 
			
		||||
        GBinderRemoteObject* self = g_object_new(THIS_TYPE, NULL);
 | 
			
		||||
        GBinderRemoteObjectPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
        self->handle = handle;
 | 
			
		||||
        if (!(self->dead = dead)) {
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, handle);
 | 
			
		||||
        switch (create) {
 | 
			
		||||
        case REMOTE_OBJECT_CREATE_DEAD:
 | 
			
		||||
            self->dead = TRUE;
 | 
			
		||||
            break;
 | 
			
		||||
        case REMOTE_OBJECT_CREATE_ACQUIRED:
 | 
			
		||||
            priv->acquired = TRUE;
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
        case REMOTE_OBJECT_CREATE_ALIVE:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!self->dead) {
 | 
			
		||||
            gbinder_ipc_looper_check(self->ipc); /* For death notifications */
 | 
			
		||||
            if (priv->acquired) {
 | 
			
		||||
                gbinder_driver_acquire(ipc->driver, handle);
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        }
 | 
			
		||||
        return self;
 | 
			
		||||
@@ -164,7 +201,7 @@ gbinder_remote_object_ref(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_ref(GBINDER_REMOTE_OBJECT(self));
 | 
			
		||||
        g_object_ref(THIS(self));
 | 
			
		||||
        return self;
 | 
			
		||||
    } else {
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -176,7 +213,7 @@ gbinder_remote_object_unref(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_unref(GBINDER_REMOTE_OBJECT(self));
 | 
			
		||||
        g_object_unref(THIS(self));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -228,7 +265,7 @@ gbinder_remote_object_init(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
 | 
			
		||||
        GBINDER_TYPE_REMOTE_OBJECT, GBinderRemoteObjectPriv);
 | 
			
		||||
        THIS_TYPE, GBinderRemoteObjectPriv);
 | 
			
		||||
 | 
			
		||||
    priv->context = g_main_context_default();
 | 
			
		||||
    self->priv = priv;
 | 
			
		||||
@@ -237,29 +274,32 @@ gbinder_remote_object_init(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_dispose(
 | 
			
		||||
    GObject* remote)
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* self = GBINDER_REMOTE_OBJECT(remote);
 | 
			
		||||
    GBinderRemoteObject* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_remote_object_disposed(self->ipc, self);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_remote_object_parent_class)->dispose(remote);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->dispose(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_finalize(
 | 
			
		||||
    GObject* remote)
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* self = GBINDER_REMOTE_OBJECT(remote);
 | 
			
		||||
    GBinderRemoteObject* self = THIS(object);
 | 
			
		||||
    GBinderRemoteObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderIpc* ipc = self->ipc;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
    if (!self->dead) {
 | 
			
		||||
        gbinder_driver_clear_death_notification(driver, self);
 | 
			
		||||
        gbinder_driver_release(driver, self->handle);
 | 
			
		||||
        if (priv->acquired) {
 | 
			
		||||
            gbinder_driver_release(driver, self->handle);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_remote_object_parent_class)->finalize(remote);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -51,11 +51,17 @@ struct gbinder_remote_object {
 | 
			
		||||
#define gbinder_remote_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
 | 
			
		||||
#define gbinder_remote_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
 | 
			
		||||
 | 
			
		||||
typedef enum gbinder_remote_object_create {
 | 
			
		||||
    REMOTE_OBJECT_CREATE_DEAD,
 | 
			
		||||
    REMOTE_OBJECT_CREATE_ALIVE,
 | 
			
		||||
    REMOTE_OBJECT_CREATE_ACQUIRED
 | 
			
		||||
} REMOTE_OBJECT_CREATE;
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_remote_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean maybe_dead)
 | 
			
		||||
    REMOTE_OBJECT_CREATE create)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
@@ -68,6 +74,11 @@ gbinder_remote_object_handle_death_notification(
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_commit_suicide(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_REMOTE_OBJECT_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -117,11 +117,23 @@ gbinder_remote_reply_is_empty(
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_remote_reply_copy_to_local(
 | 
			
		||||
    GBinderRemoteReply* self)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_remote_reply_convert_to_local(self, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_remote_reply_convert_to_local(
 | 
			
		||||
    GBinderRemoteReply* self,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* d = &self->data;
 | 
			
		||||
        GBinderObjectRegistry* reg = d->reg;
 | 
			
		||||
 | 
			
		||||
        return gbinder_local_reply_new_from_data(d->buffer);
 | 
			
		||||
        if (reg) {
 | 
			
		||||
            return gbinder_local_reply_set_contents
 | 
			
		||||
                (gbinder_local_reply_new(reg->io), d->buffer, convert);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -42,6 +42,12 @@ gbinder_remote_reply_new(
 | 
			
		||||
    GBinderObjectRegistry* reg)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_remote_reply_convert_to_local(
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_reply_set_data(
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -34,8 +34,10 @@
 | 
			
		||||
#include "gbinder_reader_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_object_converter.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
@@ -85,7 +87,33 @@ gbinder_remote_request_copy_to_local(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* d = &self->data;
 | 
			
		||||
 | 
			
		||||
        return gbinder_local_request_new_from_data(d->buffer);
 | 
			
		||||
        return gbinder_local_request_new_from_data(d->buffer, NULL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_remote_request_convert_to_local(
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* data = &self->data;
 | 
			
		||||
 | 
			
		||||
        if (!convert || convert->protocol == self->protocol) {
 | 
			
		||||
            /* The same protocol, the same format of RPC header */
 | 
			
		||||
            return gbinder_local_request_new_from_data(data->buffer, convert);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Need to translate to another format */
 | 
			
		||||
            GBinderLocalRequest* local = gbinder_local_request_new_iface
 | 
			
		||||
                (convert->io, convert->protocol, self->iface);
 | 
			
		||||
 | 
			
		||||
            gbinder_local_request_append_contents(local, data->buffer,
 | 
			
		||||
                self->header_size, convert);
 | 
			
		||||
            return local;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -56,6 +56,12 @@ gbinder_remote_request_set_data(
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_remote_request_convert_to_local(
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_REMOTE_REQUEST_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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
 | 
			
		||||
@@ -33,6 +33,12 @@
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
 | 
			
		||||
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
 | 
			
		||||
#define UNSET_WORK_SOURCE (-1)
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcProtocol callbacks (see Parcel::writeInterfaceToken in Android)
 | 
			
		||||
@@ -40,19 +46,39 @@
 | 
			
		||||
 *
 | 
			
		||||
 *   platform/system/libhwbinder/Parcel.cpp
 | 
			
		||||
 *   platform/frameworks/native/libs/binder/Parcel.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * which mutate from version to version. Specific device => protocol
 | 
			
		||||
 * mapping can be optionally configured in /etc/gbinder.conf file.
 | 
			
		||||
 * The default protocol configuration looks like this:
 | 
			
		||||
 *
 | 
			
		||||
 *   [Protocol]
 | 
			
		||||
 *   Default = aidl
 | 
			
		||||
 *   /dev/binder = aidl
 | 
			
		||||
 *   /dev/hwbinder = hidl
 | 
			
		||||
 *
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define CONF_GROUP GBINDER_CONFIG_GROUP_PROTOCOL
 | 
			
		||||
#define CONF_DEFAULT GBINDER_CONFIG_VALUE_DEFAULT
 | 
			
		||||
 | 
			
		||||
static GHashTable* gbinder_rpc_protocol_map = NULL;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Default protocol for those binder devices which which haven't been
 | 
			
		||||
 * explicitely mapped.
 | 
			
		||||
 */
 | 
			
		||||
#define DEFAULT_PROTOCOL gbinder_rpc_protocol_aidl
 | 
			
		||||
static const GBinderRpcProtocol DEFAULT_PROTOCOL;
 | 
			
		||||
static const GBinderRpcProtocol* gbinder_rpc_protocol_default =
 | 
			
		||||
    &DEFAULT_PROTOCOL;
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * /dev/binder
 | 
			
		||||
 * The original AIDL protocol.
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
/* No idea what that is... */
 | 
			
		||||
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
 | 
			
		||||
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_binder_write_ping(
 | 
			
		||||
gbinder_rpc_protocol_aidl_write_ping(
 | 
			
		||||
    GBinderWriter* writer)
 | 
			
		||||
{
 | 
			
		||||
    /* No payload */
 | 
			
		||||
@@ -60,7 +86,7 @@ gbinder_rpc_protocol_binder_write_ping(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_binder_write_rpc_header(
 | 
			
		||||
gbinder_rpc_protocol_aidl_write_rpc_header(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
@@ -75,7 +101,7 @@ gbinder_rpc_protocol_binder_write_rpc_header(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_binder_read_rpc_header(
 | 
			
		||||
gbinder_rpc_protocol_aidl_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
@@ -91,13 +117,69 @@ gbinder_rpc_protocol_binder_read_rpc_header(
 | 
			
		||||
    return *iface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GBinderRpcProtocol gbinder_rpc_protocol_aidl = {
 | 
			
		||||
    .name = "aidl",
 | 
			
		||||
    .ping_tx = GBINDER_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_aidl_write_ping,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_aidl_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_aidl_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * /dev/hwbinder
 | 
			
		||||
 * AIDL protocol appeared in Android 10 (API level 29)
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_hwbinder_write_rpc_header(
 | 
			
		||||
gbinder_rpc_protocol_aidl2_write_rpc_header(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * writeInt32(IPCThreadState::self()->getStrictModePolicy() |
 | 
			
		||||
     *               STRICT_MODE_PENALTY_GATHER);
 | 
			
		||||
     * writeInt32(IPCThreadState::kUnsetWorkSource);
 | 
			
		||||
     * writeString16(interface);
 | 
			
		||||
     */
 | 
			
		||||
    gbinder_writer_append_int32(writer, BINDER_RPC_FLAGS);
 | 
			
		||||
    gbinder_writer_append_int32(writer, UNSET_WORK_SOURCE);
 | 
			
		||||
    gbinder_writer_append_string16(writer, iface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_aidl2_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
{
 | 
			
		||||
    if (txcode > GBINDER_TRANSACTION(0,0,0)) {
 | 
			
		||||
        /* Internal transaction e.g. GBINDER_DUMP_TRANSACTION etc. */
 | 
			
		||||
        *iface = NULL;
 | 
			
		||||
    } else if (gbinder_reader_read_int32(reader, NULL) /* flags */ &&
 | 
			
		||||
        gbinder_reader_read_int32(reader, NULL) /* work source */) {
 | 
			
		||||
        *iface = gbinder_reader_read_string16(reader);
 | 
			
		||||
    } else {
 | 
			
		||||
        *iface = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return *iface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GBinderRpcProtocol gbinder_rpc_protocol_aidl2 = {
 | 
			
		||||
    .name = "aidl2",
 | 
			
		||||
    .ping_tx = GBINDER_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_aidl_write_ping, /* no payload */
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_aidl2_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_aidl2_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * The original /dev/hwbinder protocol.
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_hidl_write_rpc_header(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
@@ -109,16 +191,16 @@ gbinder_rpc_protocol_hwbinder_write_rpc_header(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_hwbinder_write_ping(
 | 
			
		||||
gbinder_rpc_protocol_hidl_write_ping(
 | 
			
		||||
    GBinderWriter* writer)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_rpc_protocol_hwbinder_write_rpc_header(writer,
 | 
			
		||||
    gbinder_rpc_protocol_hidl_write_rpc_header(writer,
 | 
			
		||||
        "android.hidl.base@1.0::IBase");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_hwbinder_read_rpc_header(
 | 
			
		||||
gbinder_rpc_protocol_hidl_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
@@ -127,30 +209,126 @@ gbinder_rpc_protocol_hwbinder_read_rpc_header(
 | 
			
		||||
    return gbinder_reader_read_string8(reader);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GBinderRpcProtocol gbinder_rpc_protocol_hidl = {
 | 
			
		||||
    .name = "hidl",
 | 
			
		||||
    .ping_tx = HIDL_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_hidl_write_ping,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_hidl_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_hidl_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
/* All known protocols */
 | 
			
		||||
static const GBinderRpcProtocol* gbinder_rpc_protocol_list[] = {
 | 
			
		||||
    &gbinder_rpc_protocol_aidl,
 | 
			
		||||
    &gbinder_rpc_protocol_aidl2,
 | 
			
		||||
    &gbinder_rpc_protocol_hidl
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_rpc_protocol_find(
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    guint i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(gbinder_rpc_protocol_list); i++) {
 | 
			
		||||
        if (!g_ascii_strcasecmp(gbinder_rpc_protocol_list[i]->name, name)) {
 | 
			
		||||
            return gbinder_rpc_protocol_list[i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_map_add_default(
 | 
			
		||||
    GHashTable* map,
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    const GBinderRpcProtocol* protocol)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_hash_table_contains(map, dev)) {
 | 
			
		||||
        g_hash_table_insert(map, g_strdup(dev), (gpointer) protocol);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gconstpointer
 | 
			
		||||
gbinder_rpc_protocol_value_map(
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_rpc_protocol_find(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GHashTable*
 | 
			
		||||
gbinder_rpc_protocol_load_config()
 | 
			
		||||
{
 | 
			
		||||
    GHashTable* map = gbinder_config_load(CONF_GROUP,
 | 
			
		||||
        gbinder_rpc_protocol_value_map);
 | 
			
		||||
 | 
			
		||||
    /* Add default configuration if it's not overridden */
 | 
			
		||||
    gbinder_rpc_protocol_map_add_default(map,
 | 
			
		||||
        GBINDER_DEFAULT_BINDER, &gbinder_rpc_protocol_aidl);
 | 
			
		||||
    gbinder_rpc_protocol_map_add_default(map,
 | 
			
		||||
        GBINDER_DEFAULT_HWBINDER, &gbinder_rpc_protocol_hidl);
 | 
			
		||||
 | 
			
		||||
    return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Runs at exit */
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_exit()
 | 
			
		||||
{
 | 
			
		||||
    if (gbinder_rpc_protocol_map) {
 | 
			
		||||
        g_hash_table_destroy(gbinder_rpc_protocol_map);
 | 
			
		||||
        gbinder_rpc_protocol_map = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    /* Reset the default too, mostly for unit testing */
 | 
			
		||||
    gbinder_rpc_protocol_default = &DEFAULT_PROTOCOL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol gbinder_rpc_protocol_binder = {
 | 
			
		||||
    .ping_tx = GBINDER_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_binder_write_ping,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_binder_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_binder_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol gbinder_rpc_protocol_hwbinder = {
 | 
			
		||||
    .ping_tx = HIDL_PING_TRANSACTION,
 | 
			
		||||
    .write_ping = gbinder_rpc_protocol_hwbinder_write_ping,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_hwbinder_write_rpc_header,
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_hwbinder_read_rpc_header
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_rpc_protocol_for_device(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return (dev && !strcmp(dev, GBINDER_DEFAULT_HWBINDER)) ?
 | 
			
		||||
        &gbinder_rpc_protocol_hwbinder : &gbinder_rpc_protocol_binder;
 | 
			
		||||
    if (dev) {
 | 
			
		||||
        const GBinderRpcProtocol* protocol;
 | 
			
		||||
 | 
			
		||||
        if (!gbinder_rpc_protocol_map) {
 | 
			
		||||
            const GBinderRpcProtocol* p;
 | 
			
		||||
 | 
			
		||||
            /* One-time initialization */
 | 
			
		||||
            gbinder_rpc_protocol_map = gbinder_rpc_protocol_load_config();
 | 
			
		||||
 | 
			
		||||
            /* "Default" is a special value stored in a special variable */
 | 
			
		||||
            p = g_hash_table_lookup(gbinder_rpc_protocol_map, CONF_DEFAULT);
 | 
			
		||||
            if (p) {
 | 
			
		||||
                g_hash_table_remove(gbinder_rpc_protocol_map, CONF_DEFAULT);
 | 
			
		||||
                gbinder_rpc_protocol_default = p;
 | 
			
		||||
            } else {
 | 
			
		||||
                gbinder_rpc_protocol_default = &DEFAULT_PROTOCOL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        protocol = g_hash_table_lookup(gbinder_rpc_protocol_map, dev);
 | 
			
		||||
        if (protocol) {
 | 
			
		||||
            GDEBUG("Using %s protocol for %s", protocol->name, dev);
 | 
			
		||||
            return protocol;
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Using default protocol %s for %s",
 | 
			
		||||
            gbinder_rpc_protocol_default->name, dev);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Using default protocol %s",
 | 
			
		||||
            gbinder_rpc_protocol_default->name);
 | 
			
		||||
    }
 | 
			
		||||
    return gbinder_rpc_protocol_default;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@
 | 
			
		||||
 *   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.
 | 
			
		||||
 *      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.
 | 
			
		||||
@@ -37,11 +36,12 @@
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For whatever reason services communicating via /dev/binder
 | 
			
		||||
 * and /dev/hwbinder use slightly different RPC headers.
 | 
			
		||||
 * There are several versions of binder RPC protocol with diffferent
 | 
			
		||||
 * transaction headers and transaction codes.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct gbinder_rpc_protocol {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    guint32 ping_tx;
 | 
			
		||||
    void (*write_ping)(GBinderWriter* writer);
 | 
			
		||||
    void (*write_rpc_header)(GBinderWriter* writer, const char* iface);
 | 
			
		||||
@@ -49,15 +49,19 @@ struct gbinder_rpc_protocol {
 | 
			
		||||
        char** iface);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const GBinderRpcProtocol gbinder_rpc_protocol_binder GBINDER_INTERNAL;
 | 
			
		||||
extern const GBinderRpcProtocol gbinder_rpc_protocol_hwbinder GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Returns one of the above based on the device name */
 | 
			
		||||
const GBinderRpcProtocol*
 | 
			
		||||
gbinder_rpc_protocol_for_device(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Runs at exit, declared here strictly for unit tests */
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_exit(
 | 
			
		||||
    void)
 | 
			
		||||
    GBINDER_DESTRUCTOR
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_RPC_PROTOCOL_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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -34,6 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
@@ -47,6 +48,51 @@
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 *
 | 
			
		||||
 * Different versions of Android come with different flavors of service
 | 
			
		||||
 * managers. They are usually based on these two more or less independent
 | 
			
		||||
 * variants:
 | 
			
		||||
 *
 | 
			
		||||
 *   platform/frameworks/native/cmds/servicemanager/ServiceManager.cpp
 | 
			
		||||
 *   platform/system/hwservicemanager/ServiceManager.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * They are talking slightly different protocols which slightly mutate
 | 
			
		||||
 * from version to version. If that's not complex enough, different
 | 
			
		||||
 * kinds of service managers can be running simultaneously, serving
 | 
			
		||||
 * different binder devices. Specific device => servicemanager mapping
 | 
			
		||||
 * can be optionally configured in /etc/gbinder.conf file. The default
 | 
			
		||||
 * service manager configuration looks like this:
 | 
			
		||||
 *
 | 
			
		||||
 *   [ServiceManager]
 | 
			
		||||
 *   Default = aidl
 | 
			
		||||
 *   /dev/binder = aidl
 | 
			
		||||
 *   /dev/hwbinder = hidl
 | 
			
		||||
 *
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define CONF_GROUP GBINDER_CONFIG_GROUP_SERVICEMANAGER
 | 
			
		||||
#define CONF_DEFAULT GBINDER_CONFIG_VALUE_DEFAULT
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_type {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    GType (*get_type)(void);
 | 
			
		||||
} GBinderServiceManagerType;
 | 
			
		||||
 | 
			
		||||
static const GBinderServiceManagerType gbinder_servicemanager_types[] = {
 | 
			
		||||
    { "aidl", gbinder_servicemanager_aidl_get_type },
 | 
			
		||||
    { "aidl2", gbinder_servicemanager_aidl2_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_DEFAULT SERVICEMANAGER_TYPE_AIDL
 | 
			
		||||
 | 
			
		||||
static GHashTable* gbinder_servicemanager_map = NULL;
 | 
			
		||||
static const GBinderServiceManagerType* gbinder_servicemanager_default =
 | 
			
		||||
    SERVICEMANAGER_TYPE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
#define PRESENSE_WAIT_MS_MIN  (100)
 | 
			
		||||
#define PRESENSE_WAIT_MS_MAX  (1000)
 | 
			
		||||
#define PRESENSE_WAIT_MS_STEP (100)
 | 
			
		||||
@@ -75,9 +121,6 @@ G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager,
 | 
			
		||||
#define GBINDER_SERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER, \
 | 
			
		||||
    GBinderServiceManager)
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_CLASS(klass) \
 | 
			
		||||
    G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER, \
 | 
			
		||||
    GBinderServiceManagerClass)
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_GET_CLASS(obj) \
 | 
			
		||||
    G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER, \
 | 
			
		||||
    GBinderServiceManagerClass)
 | 
			
		||||
@@ -156,7 +199,8 @@ gbinder_servicemanager_list_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerListTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->list(data->sm);
 | 
			
		||||
    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        list(data->sm, &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -200,8 +244,9 @@ gbinder_servicemanager_get_service_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerGetServiceTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->get_service
 | 
			
		||||
            (data->sm, data->name, &data->status);
 | 
			
		||||
    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        get_service(data->sm, data->name, &data->status,
 | 
			
		||||
            &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -243,8 +288,8 @@ gbinder_servicemanager_add_service_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAddServiceTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->add_service
 | 
			
		||||
            (data->sm, data->name, data->obj);
 | 
			
		||||
    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        add_service(data->sm, data->name, data->obj, &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -408,6 +453,64 @@ gbinder_servicemanager_autorelease_cb(
 | 
			
		||||
    g_slist_free_full(list, g_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_map_add_default(
 | 
			
		||||
    GHashTable* map,
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    const GBinderServiceManagerType* type)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_hash_table_contains(map, dev)) {
 | 
			
		||||
        g_hash_table_insert(map, g_strdup(dev), (gpointer) type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gconstpointer
 | 
			
		||||
gbinder_servicemanager_value_map(
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    guint i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(gbinder_servicemanager_types); i++) {
 | 
			
		||||
        const GBinderServiceManagerType* t = gbinder_servicemanager_types + i;
 | 
			
		||||
 | 
			
		||||
        if (!g_strcmp0(name, t->name)) {
 | 
			
		||||
            return t;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GHashTable*
 | 
			
		||||
gbinder_servicemanager_load_config()
 | 
			
		||||
{
 | 
			
		||||
    GHashTable* map = gbinder_config_load(CONF_GROUP,
 | 
			
		||||
        gbinder_servicemanager_value_map);
 | 
			
		||||
 | 
			
		||||
    /* Add default configuration if it's not overridden */
 | 
			
		||||
    gbinder_servicemanager_map_add_default(map,
 | 
			
		||||
        GBINDER_DEFAULT_BINDER, SERVICEMANAGER_TYPE_AIDL);
 | 
			
		||||
    gbinder_servicemanager_map_add_default(map,
 | 
			
		||||
        GBINDER_DEFAULT_HWBINDER, SERVICEMANAGER_TYPE_HIDL);
 | 
			
		||||
 | 
			
		||||
    return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Runs at exit */
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_exit(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    if (gbinder_servicemanager_map) {
 | 
			
		||||
        g_hash_table_destroy(gbinder_servicemanager_map);
 | 
			
		||||
        gbinder_servicemanager_map = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    /* Reset the default too, mostly for unit testing */
 | 
			
		||||
    gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internal interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -424,11 +527,10 @@ gbinder_servicemanager_new_with_type(
 | 
			
		||||
        GBinderIpc* ipc;
 | 
			
		||||
 | 
			
		||||
        if (!dev) dev = klass->default_device;
 | 
			
		||||
        ipc = gbinder_ipc_new(dev, klass->rpc_protocol);
 | 
			
		||||
        ipc = gbinder_ipc_new(dev);
 | 
			
		||||
        if (ipc) {
 | 
			
		||||
            /* Create a possible dead remote object */
 | 
			
		||||
            GBinderRemoteObject* object = gbinder_ipc_get_remote_object
 | 
			
		||||
                (ipc, GBINDER_SERVICEMANAGER_HANDLE, TRUE);
 | 
			
		||||
            /* Create a (possibly) dead service manager object */
 | 
			
		||||
            GBinderRemoteObject* object = gbinder_ipc_get_service_manager(ipc);
 | 
			
		||||
 | 
			
		||||
            if (object) {
 | 
			
		||||
                gboolean first_ref;
 | 
			
		||||
@@ -517,11 +619,35 @@ GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(dev, GBINDER_DEFAULT_HWBINDER)) {
 | 
			
		||||
        return gbinder_hwservicemanager_new(dev);
 | 
			
		||||
    } else {
 | 
			
		||||
        return gbinder_defaultservicemanager_new(dev);
 | 
			
		||||
    if (dev) {
 | 
			
		||||
        const GBinderServiceManagerType* type = NULL;
 | 
			
		||||
 | 
			
		||||
        if (!gbinder_servicemanager_map) {
 | 
			
		||||
            const GBinderServiceManagerType* t;
 | 
			
		||||
 | 
			
		||||
            /* One-time initialization */
 | 
			
		||||
            gbinder_servicemanager_map = gbinder_servicemanager_load_config();
 | 
			
		||||
 | 
			
		||||
            /* "Default" is a special value stored in a special variable */
 | 
			
		||||
            t = g_hash_table_lookup(gbinder_servicemanager_map, CONF_DEFAULT);
 | 
			
		||||
            if (t) {
 | 
			
		||||
                g_hash_table_remove(gbinder_servicemanager_map, CONF_DEFAULT);
 | 
			
		||||
                gbinder_servicemanager_default = t;
 | 
			
		||||
            } else {
 | 
			
		||||
                gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        type = g_hash_table_lookup(gbinder_servicemanager_map, dev);
 | 
			
		||||
        if (type) {
 | 
			
		||||
            GDEBUG("Using %s service manager for %s", type->name, dev);
 | 
			
		||||
        } else {
 | 
			
		||||
            type = gbinder_servicemanager_default;
 | 
			
		||||
            GDEBUG("Using default service manager %s for %s", type->name, dev);
 | 
			
		||||
        }
 | 
			
		||||
        return gbinder_servicemanager_new_with_type(type->get_type(), dev);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
@@ -651,7 +777,8 @@ gbinder_servicemanager_list_sync(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->list(self);
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            list(self, &gbinder_ipc_sync_main);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -690,8 +817,8 @@ gbinder_servicemanager_get_service_sync(
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self) && name) {
 | 
			
		||||
        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->get_service
 | 
			
		||||
            (self, name, status);
 | 
			
		||||
        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            get_service(self, name, status, &gbinder_ipc_sync_main);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
@@ -741,8 +868,8 @@ gbinder_servicemanager_add_service_sync(
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && name && obj) {
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->add_service
 | 
			
		||||
            (self, name, obj);
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            add_service(self, name, obj, &gbinder_ipc_sync_main);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
@@ -870,6 +997,28 @@ gbinder_servicemanager_remove_handlers(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * These two exist mostly for backward compatibility. Normally,
 | 
			
		||||
 * gbinder_servicemanager_new() should be used, to allow the type of
 | 
			
		||||
 * service manager to be configurable per device via /etc/gbinder.conf
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type
 | 
			
		||||
        (gbinder_servicemanager_aidl_get_type(), dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type
 | 
			
		||||
        (gbinder_servicemanager_hidl_get_type(), dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										343
									
								
								src/gbinder_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								src/gbinder_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,343 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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:
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define GLIB_DISABLE_DEPRECATION_WARNINGS
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_aidl.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_aidl_watch {
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    char* name;
 | 
			
		||||
    gulong handler_id;
 | 
			
		||||
    GBinderEventLoopTimeout* notify;
 | 
			
		||||
} GBinderServiceManagerAidlWatch;
 | 
			
		||||
 | 
			
		||||
struct gbinder_servicemanager_aidl_priv {
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderServiceManagerAidl,
 | 
			
		||||
    gbinder_servicemanager_aidl,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_servicemanager_aidl_parent_class
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_watch_proc(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidlWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(name_added, watch->name)) {
 | 
			
		||||
        GBinderServiceManager* manager =
 | 
			
		||||
            gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
 | 
			
		||||
        if (watch->notify) {
 | 
			
		||||
            gbinder_timeout_remove(watch->notify);
 | 
			
		||||
            watch->notify = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_service_registered(manager, name_added);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_aidl_watch_notify(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidlWatch* watch = user_data;
 | 
			
		||||
    GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
    char* name = g_strdup(watch->name);
 | 
			
		||||
 | 
			
		||||
    GASSERT(watch->notify);
 | 
			
		||||
    watch->notify = NULL;
 | 
			
		||||
    gbinder_servicemanager_service_registered(manager, name);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_watch_free(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidlWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_timeout_remove(watch->notify);
 | 
			
		||||
    gbinder_servicepoll_remove_handler(watch->poll, watch->handler_id);
 | 
			
		||||
    gbinder_servicepoll_unref(watch->poll);
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    g_slice_free(GBinderServiceManagerAidlWatch, watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderServiceManagerAidlWatch*
 | 
			
		||||
gbinder_servicemanager_aidl_watch_new(
 | 
			
		||||
    GBinderServiceManagerAidl* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv = self->priv;
 | 
			
		||||
    GBinderServiceManagerAidlWatch* watch =
 | 
			
		||||
        g_slice_new0(GBinderServiceManagerAidlWatch);
 | 
			
		||||
 | 
			
		||||
    watch->name = g_strdup(name);
 | 
			
		||||
    watch->poll = gbinder_servicepoll_new(&self->manager, &priv->poll);
 | 
			
		||||
    watch->handler_id = gbinder_servicepoll_add_handler(priv->poll,
 | 
			
		||||
        gbinder_servicemanager_aidl_watch_proc, watch);
 | 
			
		||||
    return watch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_aidl_list_services_req(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    gint32 index)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_int32(req, index);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_aidl_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);
 | 
			
		||||
    gbinder_local_request_append_int32(req, 0);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_aidl_list(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
    GBinderClient* client = manager->client;
 | 
			
		||||
    GBinderServiceManagerAidlClass* klass =
 | 
			
		||||
        GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(manager);
 | 
			
		||||
    GBinderLocalRequest* req = klass->list_services_req(client, 0);
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
 | 
			
		||||
    while ((reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        LIST_SERVICES_TRANSACTION, req, NULL, api)) != NULL) {
 | 
			
		||||
        char* service = gbinder_remote_reply_read_string16(reply);
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_unref(reply);
 | 
			
		||||
        if (service) {
 | 
			
		||||
            g_ptr_array_add(list, service);
 | 
			
		||||
            gbinder_local_request_unref(req);
 | 
			
		||||
            req = klass->list_services_req(client, list->len);
 | 
			
		||||
        } else {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_ptr_array_add(list, NULL);
 | 
			
		||||
    return (char**)g_ptr_array_free(list, FALSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_servicemanager_aidl_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* obj;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
        CHECK_SERVICE_TRANSACTION, req, status, api);
 | 
			
		||||
 | 
			
		||||
    obj = gbinder_remote_reply_read_object(reply);
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_servicemanager_aidl_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
    GBinderClient* client = manager->client;
 | 
			
		||||
    GBinderLocalRequest* req = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS
 | 
			
		||||
        (manager)->add_service_req(client, name, obj);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        ADD_SERVICE_TRANSACTION, req, &status, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
gbinder_servicemanager_aidl_check_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_aidl_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(manager);
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv = self->priv;
 | 
			
		||||
    GBinderServiceManagerAidlWatch* watch =
 | 
			
		||||
        gbinder_servicemanager_aidl_watch_new(self, name);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_replace(priv->watch_table, watch->name, watch);
 | 
			
		||||
    if (gbinder_servicepoll_is_known_name(watch->poll, name)) {
 | 
			
		||||
        watch->notify = gbinder_idle_add
 | 
			
		||||
            (gbinder_servicemanager_aidl_watch_notify, watch);
 | 
			
		||||
    }
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(manager);
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    g_hash_table_remove(priv->watch_table, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_init(
 | 
			
		||||
    GBinderServiceManagerAidl* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
 | 
			
		||||
        GBINDER_TYPE_SERVICEMANAGER_AIDL, GBinderServiceManagerAidlPriv);
 | 
			
		||||
 | 
			
		||||
    self->priv = priv;
 | 
			
		||||
    priv->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        NULL, gbinder_servicemanager_aidl_watch_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(object);
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(priv->watch_table);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl_class_init(
 | 
			
		||||
    GBinderServiceManagerAidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass);
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    g_type_class_add_private(klass, sizeof(GBinderServiceManagerAidlPriv));
 | 
			
		||||
    klass->list_services_req = gbinder_servicemanager_aidl_list_services_req;
 | 
			
		||||
    klass->add_service_req = gbinder_servicemanager_aidl_add_service_req;
 | 
			
		||||
 | 
			
		||||
    manager->iface = SERVICEMANAGER_AIDL_IFACE;
 | 
			
		||||
    manager->default_device = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
 | 
			
		||||
    manager->list = gbinder_servicemanager_aidl_list;
 | 
			
		||||
    manager->get_service = gbinder_servicemanager_aidl_get_service;
 | 
			
		||||
    manager->add_service = gbinder_servicemanager_aidl_add_service;
 | 
			
		||||
    manager->check_name = gbinder_servicemanager_aidl_check_name;
 | 
			
		||||
    /* normalize_name is not needed */
 | 
			
		||||
    manager->watch = gbinder_servicemanager_aidl_watch;
 | 
			
		||||
    manager->unwatch = gbinder_servicemanager_aidl_unwatch;
 | 
			
		||||
 | 
			
		||||
    object->finalize = gbinder_servicemanager_aidl_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										66
									
								
								src/gbinder_servicemanager_aidl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/gbinder_servicemanager_aidl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_SERVICEMANAGER_AIDL_H
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_AIDL_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_aidl_priv GBinderServiceManagerAidlPriv;
 | 
			
		||||
typedef struct gbinder_servicemanager_aidl {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GBinderServiceManagerAidlPriv* priv;
 | 
			
		||||
} GBinderServiceManagerAidl;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_aidl_class {
 | 
			
		||||
    GBinderServiceManagerClass parent;
 | 
			
		||||
    GBinderLocalRequest* (*list_services_req)
 | 
			
		||||
        (GBinderClient* client, gint32 index);
 | 
			
		||||
    GBinderLocalRequest* (*add_service_req)
 | 
			
		||||
        (GBinderClient* client, const char* name, GBinderLocalObject* obj);
 | 
			
		||||
} GBinderServiceManagerAidlClass;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_TYPE_SERVICEMANAGER_AIDL \
 | 
			
		||||
    gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_AIDL_CLASS(klass) \
 | 
			
		||||
    G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
 | 
			
		||||
    GBinderServiceManagerAidlClass)
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_AIDL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										102
									
								
								src/gbinder_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/gbinder_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 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_servicemanager_aidl.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
 | 
			
		||||
/* Variant of AIDL servicemanager appeared in Android 9 (API level 28) */
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerAidl GBinderServiceManagerAidl2;
 | 
			
		||||
typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl2Class;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderServiceManagerAidl2,
 | 
			
		||||
    gbinder_servicemanager_aidl2,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER_AIDL)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_servicemanager_aidl2_parent_class
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_DEFAULT (0x08)
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_ALL     (0x0f)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_aidl2_list_services_req(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    gint32 index)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_int32(req, index);
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_servicemanager_aidl2_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);
 | 
			
		||||
    gbinder_local_request_append_int32(req, 0);
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl2_init(
 | 
			
		||||
    GBinderServiceManagerAidl* self)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_aidl2_class_init(
 | 
			
		||||
    GBinderServiceManagerAidl2Class* cls)
 | 
			
		||||
{
 | 
			
		||||
    cls->list_services_req = gbinder_servicemanager_aidl2_list_services_req;
 | 
			
		||||
    cls->add_service_req = gbinder_servicemanager_aidl2_add_service_req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -31,10 +31,9 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_object.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
@@ -42,30 +41,30 @@
 | 
			
		||||
#include <gbinder_reader.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hwservicemanager_watch {
 | 
			
		||||
typedef struct gbinder_servicemanager_hidl_watch {
 | 
			
		||||
    char* name;
 | 
			
		||||
    GBinderLocalObject* callback;
 | 
			
		||||
} GBinderHwServiceManagerWatch;
 | 
			
		||||
} GBinderServiceManagerHidlWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderHwServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_hwservicemanager {
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderServiceManagerHidlClass;
 | 
			
		||||
typedef struct gbinder_servicemanager_hidl {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderHwServiceManager;
 | 
			
		||||
} GBinderServiceManagerHidl;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderHwServiceManager,
 | 
			
		||||
    gbinder_hwservicemanager,
 | 
			
		||||
G_DEFINE_TYPE(GBinderServiceManagerHidl,
 | 
			
		||||
    gbinder_servicemanager_hidl,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_hwservicemanager_parent_class
 | 
			
		||||
#define GBINDER_TYPE_HWSERVICEMANAGER (gbinder_hwservicemanager_get_type())
 | 
			
		||||
#define GBINDER_HWSERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_HWSERVICEMANAGER, \
 | 
			
		||||
    GBinderHwServiceManager)
 | 
			
		||||
#define PARENT_CLASS gbinder_servicemanager_hidl_parent_class
 | 
			
		||||
#define GBINDER_TYPE_SERVICEMANAGER_HIDL \
 | 
			
		||||
    gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_HIDL(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER_HIDL, \
 | 
			
		||||
    GBinderServiceManagerHidl)
 | 
			
		||||
 | 
			
		||||
enum gbinder_hwservicemanager_calls {
 | 
			
		||||
enum gbinder_servicemanager_hidl_calls {
 | 
			
		||||
    GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    ADD_TRANSACTION,
 | 
			
		||||
    GET_TRANSPORT_TRANSACTION,
 | 
			
		||||
@@ -76,18 +75,18 @@ enum gbinder_hwservicemanager_calls {
 | 
			
		||||
    REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gbinder_hwservicemanager_notifications {
 | 
			
		||||
enum gbinder_servicemanager_hidl_notifications {
 | 
			
		||||
    ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define HWSERVICEMANAGER_IFACE  "android.hidl.manager@1.0::IServiceManager"
 | 
			
		||||
#define HWSERVICEMANAGER_NOTIFICATION_IFACE \
 | 
			
		||||
#define SERVICEMANAGER_HIDL_IFACE  "android.hidl.manager@1.0::IServiceManager"
 | 
			
		||||
#define SERVICEMANAGER_HIDL_NOTIFICATION_IFACE \
 | 
			
		||||
    "android.hidl.manager@1.0::IServiceNotification"
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_handle_registration(
 | 
			
		||||
    GBinderHwServiceManager* self,
 | 
			
		||||
gbinder_servicemanager_hidl_handle_registration(
 | 
			
		||||
    GBinderServiceManagerHidl* self,
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    char* fqname = gbinder_reader_read_hidl_string(reader);
 | 
			
		||||
@@ -111,7 +110,7 @@ gbinder_hwservicemanager_handle_registration(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_hwservicemanager_notification(
 | 
			
		||||
gbinder_servicemanager_hidl_notification(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
@@ -119,22 +118,22 @@ gbinder_hwservicemanager_notification(
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(user_data);
 | 
			
		||||
    GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(user_data);
 | 
			
		||||
    const char* iface = gbinder_remote_request_interface(req);
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(iface, HWSERVICEMANAGER_NOTIFICATION_IFACE)) {
 | 
			
		||||
    if (!g_strcmp0(iface, SERVICEMANAGER_HIDL_NOTIFICATION_IFACE)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        switch (code) {
 | 
			
		||||
        case ON_REGISTRATION_TRANSACTION:
 | 
			
		||||
            GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u onRegistration",
 | 
			
		||||
            GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u onRegistration",
 | 
			
		||||
                code);
 | 
			
		||||
            gbinder_hwservicemanager_handle_registration(self, &reader);
 | 
			
		||||
            gbinder_servicemanager_hidl_handle_registration(self, &reader);
 | 
			
		||||
            *status = GBINDER_STATUS_OK;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u", code);
 | 
			
		||||
            GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u", code);
 | 
			
		||||
            *status = GBINDER_STATUS_FAILED;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -145,22 +144,15 @@ gbinder_hwservicemanager_notification(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type
 | 
			
		||||
        (gbinder_hwservicemanager_get_type(), dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_hwservicemanager_list(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
gbinder_servicemanager_hidl_list(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
 | 
			
		||||
        (self->client, LIST_TRANSACTION, req, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2
 | 
			
		||||
        (self->client, LIST_TRANSACTION, req, NULL, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    if (reply) {
 | 
			
		||||
@@ -184,10 +176,11 @@ gbinder_hwservicemanager_list(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_hwservicemanager_get_service(
 | 
			
		||||
gbinder_servicemanager_hidl_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* fqinstance,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    /* e.g. "android.hardware.radio@1.1::IRadio/slot1" */
 | 
			
		||||
    const char* sep = strchr(fqinstance, '/');
 | 
			
		||||
@@ -202,8 +195,8 @@ gbinder_hwservicemanager_get_service(
 | 
			
		||||
        gbinder_local_request_append_hidl_string(req, fqname);
 | 
			
		||||
        gbinder_local_request_append_hidl_string(req, name);
 | 
			
		||||
 | 
			
		||||
        reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
            GET_TRANSACTION, req, status);
 | 
			
		||||
        reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
            GET_TRANSACTION, req, status, api);
 | 
			
		||||
 | 
			
		||||
        if (reply) {
 | 
			
		||||
            GBinderReader reader;
 | 
			
		||||
@@ -231,10 +224,11 @@ gbinder_hwservicemanager_get_service(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_hwservicemanager_add_service(
 | 
			
		||||
gbinder_servicemanager_hidl_add_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
@@ -244,8 +238,8 @@ gbinder_hwservicemanager_add_service(
 | 
			
		||||
    gbinder_local_request_append_hidl_string(req, name);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj);
 | 
			
		||||
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        ADD_TRANSACTION, req, &status);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
        ADD_TRANSACTION, req, &status, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
@@ -254,10 +248,10 @@ gbinder_hwservicemanager_add_service(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_watch_free(
 | 
			
		||||
gbinder_servicemanager_hidl_watch_free(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManagerWatch* watch = data;
 | 
			
		||||
    GBinderServiceManagerHidlWatch* watch = data;
 | 
			
		||||
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    gbinder_local_object_drop(watch->callback);
 | 
			
		||||
@@ -266,7 +260,7 @@ gbinder_hwservicemanager_watch_free(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
gbinder_hwservicemanager_check_name(
 | 
			
		||||
gbinder_servicemanager_hidl_check_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
@@ -287,32 +281,32 @@ gbinder_hwservicemanager_check_name(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char*
 | 
			
		||||
gbinder_hwservicemanager_normalize_name(
 | 
			
		||||
gbinder_servicemanager_hidl_normalize_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    /* Slash must be there, see gbinder_hwservicemanager_check_name() above */
 | 
			
		||||
    /* Slash must be there, see gbinder_servicemanager_hidl_check_name() */
 | 
			
		||||
    return g_strndup(name, strchr(name, '/') - name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_hwservicemanager_watch(
 | 
			
		||||
gbinder_servicemanager_hidl_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(manager);
 | 
			
		||||
    GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(manager);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(manager->client);
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderHwServiceManagerWatch* watch =
 | 
			
		||||
        g_new0(GBinderHwServiceManagerWatch, 1);
 | 
			
		||||
    GBinderServiceManagerHidlWatch* watch =
 | 
			
		||||
        g_new0(GBinderServiceManagerHidlWatch, 1);
 | 
			
		||||
    gboolean success = FALSE;
 | 
			
		||||
    int status;
 | 
			
		||||
 | 
			
		||||
    watch->name = g_strdup(name);
 | 
			
		||||
    watch->callback = gbinder_servicemanager_new_local_object(manager,
 | 
			
		||||
        HWSERVICEMANAGER_NOTIFICATION_IFACE,
 | 
			
		||||
        gbinder_hwservicemanager_notification, self);
 | 
			
		||||
        SERVICEMANAGER_HIDL_NOTIFICATION_IFACE,
 | 
			
		||||
        gbinder_servicemanager_hidl_notification, self);
 | 
			
		||||
    g_hash_table_replace(self->watch_table, watch->name, watch);
 | 
			
		||||
 | 
			
		||||
    /* registerForNotifications(string fqName, string name,
 | 
			
		||||
@@ -344,28 +338,29 @@ gbinder_hwservicemanager_watch(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_unwatch(
 | 
			
		||||
gbinder_servicemanager_hidl_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_remove(GBINDER_HWSERVICEMANAGER(manager)->watch_table, name);
 | 
			
		||||
    g_hash_table_remove(GBINDER_SERVICEMANAGER_HIDL(manager)->
 | 
			
		||||
        watch_table, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_init(
 | 
			
		||||
    GBinderHwServiceManager* self)
 | 
			
		||||
gbinder_servicemanager_hidl_init(
 | 
			
		||||
    GBinderServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        NULL, gbinder_hwservicemanager_watch_free);
 | 
			
		||||
        NULL, gbinder_servicemanager_hidl_watch_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_finalize(
 | 
			
		||||
gbinder_servicemanager_hidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(object);
 | 
			
		||||
    GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->watch_table);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
@@ -373,21 +368,20 @@ gbinder_hwservicemanager_finalize(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_class_init(
 | 
			
		||||
    GBinderHwServiceManagerClass* klass)
 | 
			
		||||
gbinder_servicemanager_hidl_class_init(
 | 
			
		||||
    GBinderServiceManagerHidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = HWSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->iface = SERVICEMANAGER_HIDL_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
 | 
			
		||||
 | 
			
		||||
    klass->list = gbinder_hwservicemanager_list;
 | 
			
		||||
    klass->get_service = gbinder_hwservicemanager_get_service;
 | 
			
		||||
    klass->add_service = gbinder_hwservicemanager_add_service;
 | 
			
		||||
    klass->check_name = gbinder_hwservicemanager_check_name;
 | 
			
		||||
    klass->normalize_name = gbinder_hwservicemanager_normalize_name;
 | 
			
		||||
    klass->watch = gbinder_hwservicemanager_watch;
 | 
			
		||||
    klass->unwatch = gbinder_hwservicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_hwservicemanager_finalize;
 | 
			
		||||
    klass->list = gbinder_servicemanager_hidl_list;
 | 
			
		||||
    klass->get_service = gbinder_servicemanager_hidl_get_service;
 | 
			
		||||
    klass->add_service = gbinder_servicemanager_hidl_add_service;
 | 
			
		||||
    klass->check_name = gbinder_servicemanager_hidl_check_name;
 | 
			
		||||
    klass->normalize_name = gbinder_servicemanager_hidl_normalize_name;
 | 
			
		||||
    klass->watch = gbinder_servicemanager_hidl_watch;
 | 
			
		||||
    klass->unwatch = gbinder_servicemanager_hidl_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_servicemanager_hidl_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -39,9 +39,6 @@
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
/* As a special case, ServiceManager's handle is zero */
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_HANDLE (0)
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_priv GBinderServiceManagerPriv;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager {
 | 
			
		||||
@@ -64,15 +61,13 @@ typedef struct gbinder_servicemanager_class {
 | 
			
		||||
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    const char* default_device;
 | 
			
		||||
    const GBinderRpcProtocol* rpc_protocol;
 | 
			
		||||
 | 
			
		||||
    /* Methods (synchronous) */
 | 
			
		||||
    char** (*list)(GBinderServiceManager* self);
 | 
			
		||||
    GBinderRemoteObject* (*get_service)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name, int* status);
 | 
			
		||||
    int (*add_service)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name,
 | 
			
		||||
            GBinderLocalObject* obj);
 | 
			
		||||
    char** (*list)(GBinderServiceManager* self, const GBinderIpcSyncApi* api);
 | 
			
		||||
    GBinderRemoteObject* (*get_service)(GBinderServiceManager* self,
 | 
			
		||||
        const char* name, int* status, const GBinderIpcSyncApi* api);
 | 
			
		||||
    int (*add_service)(GBinderServiceManager* self, const char* name,
 | 
			
		||||
        GBinderLocalObject* obj, const GBinderIpcSyncApi* api);
 | 
			
		||||
 | 
			
		||||
    /* Checking/normalizing watch names */
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name)
 | 
			
		||||
@@ -86,6 +81,11 @@ typedef struct gbinder_servicemanager_class {
 | 
			
		||||
 | 
			
		||||
GType gbinder_servicemanager_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
#define GBINDER_TYPE_SERVICEMANAGER (gbinder_servicemanager_get_type())
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_CLASS(klass) \
 | 
			
		||||
    G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER, \
 | 
			
		||||
    GBinderServiceManagerClass)
 | 
			
		||||
 | 
			
		||||
#define gbinder_servicemanager_ipc(sm) gbinder_client_ipc(sm->client)
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_new_with_type(
 | 
			
		||||
@@ -99,6 +99,19 @@ gbinder_servicemanager_service_registered(
 | 
			
		||||
    const char* name)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Declared for unit tests */
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_exit(
 | 
			
		||||
    void)
 | 
			
		||||
    GBINDER_INTERNAL
 | 
			
		||||
    GBINDER_DESTRUCTOR;
 | 
			
		||||
 | 
			
		||||
/* Derived types */
 | 
			
		||||
 | 
			
		||||
GType gbinder_servicemanager_aidl_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
GType gbinder_servicemanager_aidl2_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
GType gbinder_servicemanager_hidl_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2019-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
#include "gbinder_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
@@ -40,16 +41,24 @@
 | 
			
		||||
 | 
			
		||||
/* Since 1.0.26 */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_SERVICENAME_RETRY_INTERVAL_MS (500)
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicename_priv {
 | 
			
		||||
    GBinderServiceName pub;
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    char* name;
 | 
			
		||||
    GBinderLocalObject* object;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderEventLoopTimeout* retry_timer;
 | 
			
		||||
    gulong presence_id;
 | 
			
		||||
    gulong add_call_id;
 | 
			
		||||
} GBinderServiceNamePriv;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_add_service(
 | 
			
		||||
    GBinderServiceNamePriv* priv);
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC GBinderServiceNamePriv*
 | 
			
		||||
gbinder_servicename_cast(GBinderServiceName* pub)
 | 
			
		||||
    { return G_CAST(pub, GBinderServiceNamePriv, pub); }
 | 
			
		||||
@@ -58,6 +67,18 @@ gbinder_servicename_cast(GBinderServiceName* pub)
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicename_add_service_retry(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceNamePriv* priv = user_data;
 | 
			
		||||
 | 
			
		||||
    priv->retry_timer = NULL;
 | 
			
		||||
    gbinder_servicename_add_service(priv);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_add_service_done(
 | 
			
		||||
@@ -71,6 +92,10 @@ gbinder_servicename_add_service_done(
 | 
			
		||||
    priv->add_call_id = 0;
 | 
			
		||||
    if (status) {
 | 
			
		||||
        GWARN("Error %d adding name \"%s\"", status, priv->name);
 | 
			
		||||
        gbinder_timeout_remove(priv->retry_timer);
 | 
			
		||||
        priv->retry_timer =
 | 
			
		||||
            gbinder_timeout_add(GBINDER_SERVICENAME_RETRY_INTERVAL_MS,
 | 
			
		||||
                gbinder_servicename_add_service_retry, priv);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Service \"%s\" has been registered", priv->name);
 | 
			
		||||
    }
 | 
			
		||||
@@ -97,9 +122,15 @@ gbinder_servicename_presence_handler(
 | 
			
		||||
 | 
			
		||||
    if (gbinder_servicemanager_is_present(sm)) {
 | 
			
		||||
        gbinder_servicename_add_service(priv);
 | 
			
		||||
    } else if (priv->add_call_id) {
 | 
			
		||||
        gbinder_servicemanager_cancel(priv->sm, priv->add_call_id);
 | 
			
		||||
        priv->add_call_id = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (priv->add_call_id) {
 | 
			
		||||
            gbinder_servicemanager_cancel(priv->sm, priv->add_call_id);
 | 
			
		||||
            priv->add_call_id = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (priv->retry_timer) {
 | 
			
		||||
            gbinder_timeout_remove(priv->retry_timer);
 | 
			
		||||
            priv->retry_timer = NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -158,8 +189,9 @@ gbinder_servicename_unref(
 | 
			
		||||
            gbinder_servicemanager_remove_handler(priv->sm, priv->presence_id);
 | 
			
		||||
            gbinder_servicemanager_unref(priv->sm);
 | 
			
		||||
            gbinder_local_object_unref(priv->object);
 | 
			
		||||
            gbinder_timeout_remove(priv->retry_timer);
 | 
			
		||||
            g_free(priv->name);
 | 
			
		||||
            g_slice_free(GBinderServiceName, self);
 | 
			
		||||
            gutil_slice_free(priv);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: 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
 | 
			
		||||
@@ -34,6 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +59,10 @@ gbinder_system_ioctl(
 | 
			
		||||
    int request,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    return ioctl(fd, request, data);
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    while ((ret = ioctl(fd, request, data)) < 0 && errno == EINTR);
 | 
			
		||||
    return ret >= 0 ? 0 : -errno;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -36,18 +36,23 @@
 | 
			
		||||
#include <gbinder_types.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_buffer_contents GBinderBufferContents;
 | 
			
		||||
typedef struct gbinder_buffer_contents_list GBinderBufferContentsList;
 | 
			
		||||
typedef struct gbinder_cleanup GBinderCleanup;
 | 
			
		||||
typedef struct gbinder_driver GBinderDriver;
 | 
			
		||||
typedef struct gbinder_handler GBinderHandler;
 | 
			
		||||
typedef struct gbinder_io GBinderIo;
 | 
			
		||||
typedef struct gbinder_object_converter GBinderObjectConverter;
 | 
			
		||||
typedef struct gbinder_object_registry GBinderObjectRegistry;
 | 
			
		||||
typedef struct gbinder_output_data GBinderOutputData;
 | 
			
		||||
typedef struct gbinder_proxy_object GBinderProxyObject;
 | 
			
		||||
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
 | 
			
		||||
typedef struct gbinder_servicepoll GBinderServicePoll;
 | 
			
		||||
typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
 | 
			
		||||
typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_INLINE_FUNC static inline
 | 
			
		||||
#define GBINDER_INTERNAL G_GNUC_INTERNAL
 | 
			
		||||
#define GBINDER_DESTRUCTOR __attribute__((destructor))
 | 
			
		||||
 | 
			
		||||
#define GBINDER_TRANSACTION(c2,c3,c4)     GBINDER_FOURCC('_',c2,c3,c4)
 | 
			
		||||
#define GBINDER_PING_TRANSACTION          GBINDER_TRANSACTION('P','N','G')
 | 
			
		||||
@@ -69,6 +74,9 @@ typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
 | 
			
		||||
#define HIDL_DEBUG_TRANSACTION                    HIDL_FOURCC('D','B','G')
 | 
			
		||||
#define HIDL_HASH_CHAIN_TRANSACTION               HIDL_FOURCC('H','S','H')
 | 
			
		||||
 | 
			
		||||
/* As a special case, ServiceManager's handle is zero */
 | 
			
		||||
#define GBINDER_SERVICEMANAGER_HANDLE (0)
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_TYPES_PRIVATE_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
 | 
			
		||||
@@ -32,6 +32,8 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_writer_p.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_object_converter.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
@@ -55,52 +57,88 @@ GBINDER_INLINE_FUNC GBinderWriterPriv* gbinder_writer_cast(GBinderWriter* pub)
 | 
			
		||||
GBINDER_INLINE_FUNC GBinderWriterData* gbinder_writer_data(GBinderWriter* pub)
 | 
			
		||||
    { return G_LIKELY(pub) ? gbinder_writer_cast(pub)->data : NULL; }
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_buffer_cleanup(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_buffer_contents_unref((GBinderBufferContents*)data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_set_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    gsize bufsize;
 | 
			
		||||
    const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize);
 | 
			
		||||
    const GBinderIo* io = gbinder_buffer_io(buffer);
 | 
			
		||||
    GBinderBufferContents* contents = gbinder_buffer_contents(buffer);
 | 
			
		||||
 | 
			
		||||
    GASSERT(data->io == io);
 | 
			
		||||
    g_byte_array_set_size(data->bytes, 0);
 | 
			
		||||
    gutil_int_array_set_count(data->offsets, 0);
 | 
			
		||||
    data->buffers_size = 0;
 | 
			
		||||
    gbinder_cleanup_reset(data->cleanup);
 | 
			
		||||
    gbinder_writer_data_append_contents(data, buffer, 0, convert);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    gsize off,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBufferContents* contents = gbinder_buffer_contents(buffer);
 | 
			
		||||
 | 
			
		||||
    g_byte_array_append(data->bytes, bufdata, bufsize);
 | 
			
		||||
    if (contents) {
 | 
			
		||||
        gsize bufsize;
 | 
			
		||||
        GByteArray* dest = data->bytes;
 | 
			
		||||
        const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize);
 | 
			
		||||
        void** objects = gbinder_buffer_objects(buffer);
 | 
			
		||||
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup,
 | 
			
		||||
            gbinder_writer_data_buffer_cleanup,
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup, (GDestroyNotify)
 | 
			
		||||
            gbinder_buffer_contents_unref,
 | 
			
		||||
            gbinder_buffer_contents_ref(contents));
 | 
			
		||||
        if (objects && *objects) {
 | 
			
		||||
            const GBinderIo* io = gbinder_buffer_io(buffer);
 | 
			
		||||
 | 
			
		||||
            /* GBinderIo must be the same because it's defined by the kernel */
 | 
			
		||||
            GASSERT(io == data->io);
 | 
			
		||||
            if (!data->offsets) {
 | 
			
		||||
                data->offsets = gutil_int_array_new();
 | 
			
		||||
            }
 | 
			
		||||
            while (*objects) {
 | 
			
		||||
                const guint8* obj = *objects++;
 | 
			
		||||
                gsize offset = obj - bufdata;
 | 
			
		||||
                gsize objsize = io->object_data_size(obj);
 | 
			
		||||
                gsize objsize, offset = obj - bufdata;
 | 
			
		||||
                GBinderLocalObject* local;
 | 
			
		||||
                guint32 handle;
 | 
			
		||||
 | 
			
		||||
                GASSERT(offset >= off && offset < bufsize);
 | 
			
		||||
                if (offset > off) {
 | 
			
		||||
                    /* Copy serialized data preceeding this object */
 | 
			
		||||
                    g_byte_array_append(dest, bufdata + off, offset - off);
 | 
			
		||||
                    off = offset;
 | 
			
		||||
                }
 | 
			
		||||
                /* Offset in the destination buffer */
 | 
			
		||||
                gutil_int_array_append(data->offsets, dest->len);
 | 
			
		||||
 | 
			
		||||
                /* Convert remote object into local if necessary */
 | 
			
		||||
                if (convert && io->decode_binder_handle(obj, &handle) &&
 | 
			
		||||
                    (local = gbinder_object_converter_handle_to_local
 | 
			
		||||
                    (convert, handle))) {
 | 
			
		||||
                    const guint pos = dest->len;
 | 
			
		||||
 | 
			
		||||
                    g_byte_array_set_size(dest, pos +
 | 
			
		||||
                        GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
                    objsize = io->encode_local_object(dest->data + pos, local);
 | 
			
		||||
                    g_byte_array_set_size(dest, pos + objsize);
 | 
			
		||||
 | 
			
		||||
                    /* Keep the reference */
 | 
			
		||||
                    data->cleanup = gbinder_cleanup_add(data->cleanup,
 | 
			
		||||
                        (GDestroyNotify) gbinder_local_object_unref, local);
 | 
			
		||||
                } else {
 | 
			
		||||
                    objsize = io->object_size(obj);
 | 
			
		||||
                    g_byte_array_append(dest, obj, objsize);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                GASSERT(offset > 0 && offset < bufsize);
 | 
			
		||||
                gutil_int_array_append(data->offsets, (int)offset);
 | 
			
		||||
                /* Size of each buffer has to be 8-byte aligned */
 | 
			
		||||
                data->buffers_size += G_ALIGN8(objsize);
 | 
			
		||||
                data->buffers_size += G_ALIGN8(io->object_data_size(obj));
 | 
			
		||||
                off += objsize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (off < bufsize) {
 | 
			
		||||
            /* Copy remaining data */
 | 
			
		||||
            g_byte_array_append(dest, bufdata + off, bufsize - off);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -686,13 +724,9 @@ gbinder_writer_data_append_hidl_vec(
 | 
			
		||||
    vec->owns_buffer = TRUE;
 | 
			
		||||
    data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, vec);
 | 
			
		||||
 | 
			
		||||
    /* Write the buffer object pointing to the vector descriptor */
 | 
			
		||||
    /* Every vector, even the one without data, requires two buffer objects */
 | 
			
		||||
    gbinder_writer_data_write_buffer_object(data, vec, sizeof(*vec), NULL);
 | 
			
		||||
 | 
			
		||||
    /* Not sure what's the right way to deal with NULL vectors... */
 | 
			
		||||
    if (buf) {
 | 
			
		||||
        gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -732,13 +766,14 @@ gbinder_writer_data_append_hidl_string(
 | 
			
		||||
    gbinder_writer_data_write_buffer_object(data, hidl_string,
 | 
			
		||||
        sizeof(*hidl_string), NULL);
 | 
			
		||||
 | 
			
		||||
    /* Not sure what's the right way to deal with NULL strings... */
 | 
			
		||||
    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);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -819,9 +854,16 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
                GVERBOSE_("%d. \"%s\" %u %u %u", i + 1, hidl_str->data.str,
 | 
			
		||||
                    (guint)hidl_str->len, (guint)str_parent.index,
 | 
			
		||||
                    (guint)data->buffers_size);
 | 
			
		||||
            } 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,
 | 
			
		||||
                    &str_parent);
 | 
			
		||||
            }
 | 
			
		||||
            str_parent.offset += sizeof(GBinderHidlString);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_writer_data_write_buffer_object(data, NULL, 0, &vec_parent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -54,7 +54,16 @@ gbinder_writer_init(
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_set_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    gsize data_offset,
 | 
			
		||||
    GBinderObjectConverter* convert)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
all:
 | 
			
		||||
%:
 | 
			
		||||
	@$(MAKE) -C binder-bridge $*
 | 
			
		||||
	@$(MAKE) -C binder-client $*
 | 
			
		||||
	@$(MAKE) -C binder-dump $*
 | 
			
		||||
	@$(MAKE) -C binder-list $*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										154
									
								
								test/binder-bridge/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								test/binder-bridge/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
# -*- 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 = binder-bridge
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# 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 $@
 | 
			
		||||
							
								
								
									
										196
									
								
								test/binder-bridge/binder-bridge.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								test/binder-bridge/binder-bridge.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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_log.h>
 | 
			
		||||
 | 
			
		||||
#include <glib-unix.h>
 | 
			
		||||
 | 
			
		||||
#define RET_OK      (0)
 | 
			
		||||
#define RET_NODEV   (1)
 | 
			
		||||
#define RET_INVARG  (2)
 | 
			
		||||
 | 
			
		||||
typedef struct app_options {
 | 
			
		||||
    const char* src;
 | 
			
		||||
    const char* dest;
 | 
			
		||||
    char* src_name;
 | 
			
		||||
    const char* dest_name;
 | 
			
		||||
    const char** ifaces;
 | 
			
		||||
} AppOptions;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_signal(
 | 
			
		||||
    gpointer loop)
 | 
			
		||||
{
 | 
			
		||||
    GINFO("Caught signal, shutting down...");
 | 
			
		||||
    g_main_loop_quit(loop);
 | 
			
		||||
    return G_SOURCE_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
app_run(
 | 
			
		||||
    const AppOptions* opt)
 | 
			
		||||
{
 | 
			
		||||
    int ret = RET_NODEV;
 | 
			
		||||
    GBinderServiceManager* src = gbinder_servicemanager_new(opt->src);
 | 
			
		||||
 | 
			
		||||
    if (src) {
 | 
			
		||||
        GBinderServiceManager* dest = gbinder_servicemanager_new(opt->dest);
 | 
			
		||||
 | 
			
		||||
        if (dest) {
 | 
			
		||||
            GMainLoop* loop = g_main_loop_new(NULL, TRUE);
 | 
			
		||||
            guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, loop);
 | 
			
		||||
            guint sigint = g_unix_signal_add(SIGINT, app_signal, loop);
 | 
			
		||||
            GBinderBridge* bridge = gbinder_bridge_new2
 | 
			
		||||
                (opt->src_name, opt->dest_name, opt->ifaces, src, dest);
 | 
			
		||||
 | 
			
		||||
            g_main_loop_run(loop);
 | 
			
		||||
 | 
			
		||||
            if (sigtrm) g_source_remove(sigtrm);
 | 
			
		||||
            if (sigint) g_source_remove(sigint);
 | 
			
		||||
            g_main_loop_unref(loop);
 | 
			
		||||
            gbinder_bridge_free(bridge);
 | 
			
		||||
            gbinder_servicemanager_unref(dest);
 | 
			
		||||
            ret = RET_OK;
 | 
			
		||||
        } else {
 | 
			
		||||
            GERR("No servicemanager at %s", opt->dest);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_unref(src);
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("No servicemanager at %s", opt->src);
 | 
			
		||||
    }
 | 
			
		||||
    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[] = {
 | 
			
		||||
        { "source", 's', 0, G_OPTION_ARG_STRING, &opt->src_name,
 | 
			
		||||
          "Register a different name on source", "NAME" },
 | 
			
		||||
        { "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 },
 | 
			
		||||
        { NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GError* error = NULL;
 | 
			
		||||
    GOptionContext* options = g_option_context_new("SRC DST NAME IFACES...");
 | 
			
		||||
 | 
			
		||||
    gutil_log_default.level = GLOG_LEVEL_DEFAULT;
 | 
			
		||||
 | 
			
		||||
    g_option_context_add_main_entries(options, entries, NULL);
 | 
			
		||||
    g_option_context_set_summary(options,
 | 
			
		||||
        "Forwards calls from device SRC to device DST.");
 | 
			
		||||
 | 
			
		||||
    if (g_option_context_parse(options, &argc, &argv, &error)) {
 | 
			
		||||
        if (argc >= 5) {
 | 
			
		||||
            int i;
 | 
			
		||||
            const int first_iface = 4;
 | 
			
		||||
 | 
			
		||||
            opt->src = argv[1];
 | 
			
		||||
            opt->dest = argv[2];
 | 
			
		||||
            opt->dest_name = argv[3];
 | 
			
		||||
            opt->ifaces = g_new(const char*, argc - first_iface + 1);
 | 
			
		||||
            for (i = first_iface; i < argc; i++) {
 | 
			
		||||
                opt->ifaces[i - first_iface] = argv[i];
 | 
			
		||||
            }
 | 
			
		||||
            opt->ifaces[i - first_iface] = NULL;
 | 
			
		||||
            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.src_name);
 | 
			
		||||
    g_free(opt.ifaces);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -138,3 +138,17 @@ libgbinder-debug:
 | 
			
		||||
 | 
			
		||||
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 $@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
#define RET_INVARG      (2)
 | 
			
		||||
#define RET_ERR         (3)
 | 
			
		||||
 | 
			
		||||
#define DEV_DEFAULT     "/dev/binder"
 | 
			
		||||
#define DEV_DEFAULT     GBINDER_DEFAULT_HWBINDER
 | 
			
		||||
 | 
			
		||||
typedef struct app_options {
 | 
			
		||||
    char* dev;
 | 
			
		||||
 
 | 
			
		||||
@@ -138,3 +138,17 @@ libgbinder-debug:
 | 
			
		||||
 | 
			
		||||
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 $@
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,11 @@
 | 
			
		||||
 | 
			
		||||
all:
 | 
			
		||||
%:
 | 
			
		||||
	@$(MAKE) -C unit_bridge $*
 | 
			
		||||
	@$(MAKE) -C unit_buffer $*
 | 
			
		||||
	@$(MAKE) -C unit_cleanup $*
 | 
			
		||||
	@$(MAKE) -C unit_client $*
 | 
			
		||||
	@$(MAKE) -C unit_config $*
 | 
			
		||||
	@$(MAKE) -C unit_driver $*
 | 
			
		||||
	@$(MAKE) -C unit_eventloop $*
 | 
			
		||||
	@$(MAKE) -C unit_ipc $*
 | 
			
		||||
@@ -13,11 +15,15 @@ all:
 | 
			
		||||
	@$(MAKE) -C unit_local_request $*
 | 
			
		||||
	@$(MAKE) -C unit_log $*
 | 
			
		||||
	@$(MAKE) -C unit_protocol $*
 | 
			
		||||
	@$(MAKE) -C unit_proxy_object $*
 | 
			
		||||
	@$(MAKE) -C unit_reader $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_object $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_reply $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_request $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl2 $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_hidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicename $*
 | 
			
		||||
	@$(MAKE) -C unit_servicepoll $*
 | 
			
		||||
	@$(MAKE) -C unit_writer $*
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage
 | 
			
		||||
 | 
			
		||||
CC ?= $(CROSS_COMPILE)gcc
 | 
			
		||||
LD = $(CC)
 | 
			
		||||
WARNINGS += -Wall
 | 
			
		||||
WARNINGS += -Wall -Wno-deprecated-declarations
 | 
			
		||||
INCLUDES += -I$(COMMON_DIR) -I$(LIB_DIR)/src -I$(LIB_DIR)/include
 | 
			
		||||
BASE_FLAGS = -fPIC
 | 
			
		||||
BASE_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS)
 | 
			
		||||
@@ -52,7 +52,7 @@ BASE_CFLAGS = $(BASE_FLAGS) $(CFLAGS)
 | 
			
		||||
FULL_CFLAGS = $(BASE_CFLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \
 | 
			
		||||
  $(shell pkg-config --cflags $(PKGS))
 | 
			
		||||
FULL_LDFLAGS = $(BASE_LDFLAGS)
 | 
			
		||||
LIBS = $(shell pkg-config --libs $(PKGS))
 | 
			
		||||
LIBS = $(shell pkg-config --libs $(PKGS)) -lpthread
 | 
			
		||||
QUIET_MAKE = make --no-print-directory
 | 
			
		||||
DEBUG_FLAGS = -g
 | 
			
		||||
RELEASE_FLAGS =
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
			
		||||
@@ -35,6 +35,8 @@
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder TestBinder;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_noop(
 | 
			
		||||
    int fd);
 | 
			
		||||
@@ -111,10 +113,44 @@ test_binder_br_reply_status_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status);
 | 
			
		||||
 | 
			
		||||
typedef enum test_looper {
 | 
			
		||||
    TEST_LOOPER_DISABLE,
 | 
			
		||||
    TEST_LOOPER_ENABLE,
 | 
			
		||||
    TEST_LOOPER_ENABLE_ONE
 | 
			
		||||
} TEST_LOOPER;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_looper_enabled(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean enabled);
 | 
			
		||||
    TEST_LOOPER value);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_passthrough(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean passthrough);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
test_binder_handle(
 | 
			
		||||
    int fd,
 | 
			
		||||
    GBinderLocalObject* obj);
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
test_binder_object(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint handle)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT; /* Need to unref */
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_binder_register_object(
 | 
			
		||||
    int fd,
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    guint handle);
 | 
			
		||||
 | 
			
		||||
#define AUTO_HANDLE ((guint)-1)
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_unregister_objects(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_destroy(
 | 
			
		||||
@@ -122,6 +158,11 @@ test_binder_set_destroy(
 | 
			
		||||
    gpointer ptr,
 | 
			
		||||
    GDestroyNotify destroy);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_exit_wait(
 | 
			
		||||
    const TestOpt* opt,
 | 
			
		||||
    GMainLoop* loop);
 | 
			
		||||
 | 
			
		||||
#endif /* TEST_BINDER_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: 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
 | 
			
		||||
@@ -58,6 +58,23 @@ void
 | 
			
		||||
test_quit_later(
 | 
			
		||||
    GMainLoop* loop);
 | 
			
		||||
 | 
			
		||||
/* Quits the event loop after n iterations */
 | 
			
		||||
void
 | 
			
		||||
test_quit_later_n(
 | 
			
		||||
    GMainLoop* loop,
 | 
			
		||||
    guint n);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Makes sure that we own the context for the entire duration of the test.
 | 
			
		||||
 * That prevents many race conditions - all callbacks that are supposed to
 | 
			
		||||
 * be invoked on the main thread, are actually invoked on the main thread
 | 
			
		||||
 * (rather than a random worker thread which happens to acquire the context).
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
test_run_in_context(
 | 
			
		||||
    const TestOpt* opt,
 | 
			
		||||
    GTestFunc func);
 | 
			
		||||
 | 
			
		||||
#define TEST_TIMEOUT_SEC (20)
 | 
			
		||||
 | 
			
		||||
/* Helper macros */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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
 | 
			
		||||
@@ -34,6 +34,16 @@
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
typedef struct test_quit_later_data{
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    guint n;
 | 
			
		||||
} TestQuitLaterData;
 | 
			
		||||
 | 
			
		||||
typedef struct test_context_data{
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    GTestFunc func;
 | 
			
		||||
} TestContextData;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_timeout_expired(
 | 
			
		||||
@@ -43,6 +53,46 @@ test_timeout_expired(
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_quit_later_n_free(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestQuitLaterData* data = user_data;
 | 
			
		||||
 | 
			
		||||
    g_main_loop_unref(data->loop);
 | 
			
		||||
    g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_quit_later_n_func(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestQuitLaterData* data = user_data;
 | 
			
		||||
 | 
			
		||||
    if (data->n > 0) {
 | 
			
		||||
        data->n--;
 | 
			
		||||
        return G_SOURCE_CONTINUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        g_main_loop_quit(data->loop);
 | 
			
		||||
        return G_SOURCE_REMOVE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_quit_later_n(
 | 
			
		||||
    GMainLoop* loop,
 | 
			
		||||
    guint n)
 | 
			
		||||
{
 | 
			
		||||
    TestQuitLaterData* data = g_new0(TestQuitLaterData, 1);
 | 
			
		||||
 | 
			
		||||
    data->loop = g_main_loop_ref(loop);
 | 
			
		||||
    data->n = n;
 | 
			
		||||
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_quit_later_n_func, data,
 | 
			
		||||
        test_quit_later_n_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_quit_later_cb(
 | 
			
		||||
@@ -59,6 +109,40 @@ test_quit_later(
 | 
			
		||||
    g_idle_add(test_quit_later_cb, loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_run_in_context_cb(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestContextData* test = data;
 | 
			
		||||
 | 
			
		||||
    test->func();
 | 
			
		||||
    g_main_loop_quit(test->loop);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_run_in_context(
 | 
			
		||||
    const TestOpt* opt,
 | 
			
		||||
    GTestFunc func)
 | 
			
		||||
{
 | 
			
		||||
    TestContextData test;
 | 
			
		||||
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    test.func = func;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * This makes sure that we own the context for the entire duration
 | 
			
		||||
     * of the test. That prevents many race conditions - all callbacks
 | 
			
		||||
     * that are supposed to be invoked on the main thread, are actually
 | 
			
		||||
     * invoked on the main thread (rather than a random worker thread
 | 
			
		||||
     * which happens to acquire the context).
 | 
			
		||||
     */
 | 
			
		||||
    g_idle_add(test_run_in_context_cb, &test);
 | 
			
		||||
    test_run(opt, test.loop);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_run(
 | 
			
		||||
    const TestOpt* opt,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										567
									
								
								unit/common/test_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								unit/common/test_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,567 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "test_servicemanager_hidl.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_remote_object.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
#include "gbinder_client.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test service manager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define BASE_IFACE "android.hidl.base@1.0::IBase"
 | 
			
		||||
#define MANAGER_IFACE "android.hidl.manager@1.0::IServiceManager"
 | 
			
		||||
#define NOTIFICATION_IFACE "android.hidl.manager@1.0::IServiceNotification"
 | 
			
		||||
 | 
			
		||||
const char* const servicemanager_hidl_ifaces[] = { MANAGER_IFACE, NULL };
 | 
			
		||||
 | 
			
		||||
enum servicemanager_hidl_tx {
 | 
			
		||||
    GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    ADD_TRANSACTION,
 | 
			
		||||
    GET_TRANSPORT_TRANSACTION,
 | 
			
		||||
    LIST_TRANSACTION,
 | 
			
		||||
    LIST_BY_INTERFACE_TRANSACTION,
 | 
			
		||||
    REGISTER_FOR_NOTIFICATIONS_TRANSACTION,
 | 
			
		||||
    DEBUG_DUMP_TRANSACTION,
 | 
			
		||||
    REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum servicemanager_hidl_notify_tx {
 | 
			
		||||
    ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass TestServiceManagerHidlClass;
 | 
			
		||||
struct test_servicemanager_hidl {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    GPtrArray* watchers;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct test_servicemanager_hidl_add {
 | 
			
		||||
    TestServiceManagerHidl* manager;
 | 
			
		||||
    GBinderRemoteObject* object;
 | 
			
		||||
    char* instance;
 | 
			
		||||
} TestServiceManagerHidlAdd;
 | 
			
		||||
 | 
			
		||||
struct test_servicemanager_hidl_watcher {
 | 
			
		||||
    GBinderClient* client;
 | 
			
		||||
    char* iface;
 | 
			
		||||
    char* name;
 | 
			
		||||
} TestServiceManagerHidlWatcher;
 | 
			
		||||
 | 
			
		||||
#define THIS_TYPE test_servicemanager_hidl_get_type()
 | 
			
		||||
#define PARENT_CLASS test_servicemanager_hidl_parent_class
 | 
			
		||||
#define THIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), THIS_TYPE, \
 | 
			
		||||
    TestServiceManagerHidl))
 | 
			
		||||
G_DEFINE_TYPE(TestServiceManagerHidl, test_servicemanager_hidl, \
 | 
			
		||||
    GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_notify(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderClient* watcher,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    const char* instance,
 | 
			
		||||
    gboolean preexisting)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* notify = gbinder_client_new_request(watcher);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    char* iface2 = g_strdup(iface);
 | 
			
		||||
    char* instance2 = g_strdup(instance);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(notify, &writer);
 | 
			
		||||
    gbinder_writer_append_hidl_string(&writer, iface2);
 | 
			
		||||
    gbinder_writer_append_hidl_string(&writer, instance2);
 | 
			
		||||
    gbinder_writer_append_bool(&writer, preexisting);
 | 
			
		||||
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, g_free, iface2);
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, g_free, instance2);
 | 
			
		||||
 | 
			
		||||
    gbinder_client_transact(watcher, ON_REGISTRATION_TRANSACTION,
 | 
			
		||||
        GBINDER_TX_FLAG_ONEWAY, notify, NULL, NULL, NULL);
 | 
			
		||||
    gbinder_local_request_unref(notify);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_notify_all(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    const char* instance,
 | 
			
		||||
    gboolean preexisting)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray* watchers = self->watchers;
 | 
			
		||||
    guint i;
 | 
			
		||||
 | 
			
		||||
    /* For unit test purposes, just always notify all watchers */
 | 
			
		||||
    for (i = 0; i < watchers->len; i++) {
 | 
			
		||||
        test_servicemanager_hidl_notify(self, watchers->pdata[i],
 | 
			
		||||
            iface, instance, preexisting);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_add_complete2(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidlAdd* add = user_data;
 | 
			
		||||
    TestServiceManagerHidl* self = add->manager;
 | 
			
		||||
    const char* instance = add->instance;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    /* Remove the temporary entry */
 | 
			
		||||
    GDEBUG("Dropping '%s'", instance);
 | 
			
		||||
    g_hash_table_remove(self->objects, instance);
 | 
			
		||||
    if (reply) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
        gint32 status;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
        if (gbinder_reader_read_int32(&reader, &status) && status == 0) {
 | 
			
		||||
            char** ifaces = gbinder_reader_read_hidl_string_vec(&reader);
 | 
			
		||||
 | 
			
		||||
            if (ifaces) {
 | 
			
		||||
                char** ptr = ifaces;
 | 
			
		||||
 | 
			
		||||
                while (*ptr) {
 | 
			
		||||
                    const char* iface = *ptr++;
 | 
			
		||||
                    char* fqinstance = g_strconcat(iface, "/", instance, NULL);
 | 
			
		||||
 | 
			
		||||
                    /* Add permanent entries */
 | 
			
		||||
                    GDEBUG("Adding '%s'", fqinstance);
 | 
			
		||||
                    g_hash_table_replace(self->objects, fqinstance,
 | 
			
		||||
                        gbinder_remote_object_ref(add->object));
 | 
			
		||||
                    test_servicemanager_hidl_notify_all(self, iface,
 | 
			
		||||
                        instance, FALSE);
 | 
			
		||||
                }
 | 
			
		||||
                g_strfreev(ifaces);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_add_done(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidlAdd* add = data;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_object_unref(add->object);
 | 
			
		||||
    g_object_unref(add->manager);
 | 
			
		||||
    g_free(add->instance);
 | 
			
		||||
    g_free(add);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_servicemanager_hidl_add_complete(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidlAdd* add = data;
 | 
			
		||||
    GBinderClient* client = gbinder_client_new(add->object, BASE_IFACE);
 | 
			
		||||
 | 
			
		||||
    gbinder_client_transact(client, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, 0, NULL,
 | 
			
		||||
        test_servicemanager_hidl_add_complete2,
 | 
			
		||||
        test_servicemanager_hidl_add_done, add);
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Call handlers
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_get(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    const char* ifname;
 | 
			
		||||
    const char* instance;
 | 
			
		||||
    char* fqinstance;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert((ifname = gbinder_reader_read_hidl_string_c(&reader)));
 | 
			
		||||
    g_assert((instance = gbinder_reader_read_hidl_string_c(&reader)));
 | 
			
		||||
    fqinstance = g_strconcat(ifname, "/", instance, NULL);
 | 
			
		||||
 | 
			
		||||
    remote_obj = g_hash_table_lookup(self->objects, fqinstance);
 | 
			
		||||
    if (!remote_obj) {
 | 
			
		||||
        remote_obj = g_hash_table_lookup(self->objects, instance);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
    if (remote_obj) {
 | 
			
		||||
        GDEBUG("Found name '%s' => %p", fqinstance, remote_obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Name '%s' not found", fqinstance);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_local_reply_append_remote_object(reply, remote_obj);
 | 
			
		||||
    g_free(fqinstance);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_add(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    const char* instance;
 | 
			
		||||
    gboolean success;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    instance = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    remote_obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
 | 
			
		||||
    if (instance && remote_obj) {
 | 
			
		||||
        const char* sep = strrchr(instance, '/');
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Adding '%s'", instance);
 | 
			
		||||
        g_hash_table_replace(self->objects, g_strdup(instance), remote_obj);
 | 
			
		||||
        if (sep) {
 | 
			
		||||
            /* Alread know the interface */
 | 
			
		||||
            char* iface = g_strndup(instance, sep - instance);
 | 
			
		||||
 | 
			
		||||
            test_servicemanager_hidl_notify_all(self, iface, sep + 1, FALSE);
 | 
			
		||||
            g_free(iface);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Query interface chain on the main thread */
 | 
			
		||||
            TestServiceManagerHidlAdd* add = g_new(TestServiceManagerHidlAdd,1);
 | 
			
		||||
 | 
			
		||||
            g_object_ref(add->manager = self);
 | 
			
		||||
            add->object = gbinder_remote_object_ref(remote_obj);
 | 
			
		||||
            add->instance = g_strdup(instance);
 | 
			
		||||
            g_idle_add(test_servicemanager_hidl_add_complete, add);
 | 
			
		||||
        }
 | 
			
		||||
        success = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
        success = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_append_bool(reply, success);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_list(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GHashTableIter it;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    gpointer key;
 | 
			
		||||
    char** list = NULL;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    g_hash_table_iter_init(&it, self->objects);
 | 
			
		||||
    while (g_hash_table_iter_next(&it, &key, NULL)) {
 | 
			
		||||
        list = gutil_strv_add(list, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, 0);
 | 
			
		||||
    gbinder_writer_append_hidl_string_vec(&writer, (const char**) list, -1);
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, (GDestroyNotify) g_strfreev, list);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_register_for_notifications(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* watcher;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    const char* instance;
 | 
			
		||||
    gboolean success;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    iface = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    instance = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    watcher = gbinder_reader_read_object(&reader);
 | 
			
		||||
 
 | 
			
		||||
    if (watcher) {
 | 
			
		||||
        GBinderClient* wc = gbinder_client_new(watcher, NOTIFICATION_IFACE);
 | 
			
		||||
        GHashTableIter it;
 | 
			
		||||
        gpointer key;
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Registering watcher %s/%s", iface, instance);
 | 
			
		||||
        g_ptr_array_add(self->watchers, wc);
 | 
			
		||||
        gbinder_remote_object_unref(watcher); /* Client keeps the reference */
 | 
			
		||||
 | 
			
		||||
        /* Send notifications for pre-existing services */
 | 
			
		||||
        g_hash_table_iter_init(&it, self->objects);
 | 
			
		||||
        while (g_hash_table_iter_next(&it, &key, NULL)) {
 | 
			
		||||
            const char* name = key;
 | 
			
		||||
            const char* sep = strrchr(name, '/');
 | 
			
		||||
 | 
			
		||||
            if (sep) {
 | 
			
		||||
                char* intf = g_strndup(name, sep - name);
 | 
			
		||||
 | 
			
		||||
                test_servicemanager_hidl_notify(self, wc, intf, sep + 1, TRUE);
 | 
			
		||||
                g_free(intf);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        success = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        success = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, 0);
 | 
			
		||||
    gbinder_local_reply_append_bool(reply, success);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = THIS(user_data);
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,MANAGER_IFACE);
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GET_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_get(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case ADD_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_add(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIST_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_list(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case REGISTER_FOR_NOTIFICATIONS_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_register_for_notifications(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_hidl_new(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = g_object_new(THIS_TYPE, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_init_base(obj, ipc, servicemanager_hidl_ifaces,
 | 
			
		||||
        test_servicemanager_hidl_handler, self);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_free(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
test_servicemanager_hidl_ipc(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    return self ? self->parent.ipc : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_servicemanager_hidl_object_count(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    guint count = 0;
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        g_mutex_lock(&self->mutex);
 | 
			
		||||
        count = g_hash_table_size(self->objects);
 | 
			
		||||
        g_mutex_unlock(&self->mutex);
 | 
			
		||||
    }
 | 
			
		||||
    return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_hidl_lookup(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* object = NULL;
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        g_mutex_lock(&self->mutex);
 | 
			
		||||
        object = g_hash_table_lookup(self->objects, name);
 | 
			
		||||
        g_mutex_unlock(&self->mutex);
 | 
			
		||||
    }
 | 
			
		||||
    return object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
test_servicemanager_hidl_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * Handle all transactions on the looper thread to avoid deadlocks.
 | 
			
		||||
     * Unlike the real situation, binder simulation has one main thread
 | 
			
		||||
     * shared by both sides of the binder socket connection.
 | 
			
		||||
     */
 | 
			
		||||
    return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_handle_looper_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(gbinder_remote_request_interface(req), MANAGER_IFACE)) {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
    g_mutex_clear(&self->mutex);
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    g_ptr_array_free(self->watchers, TRUE);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_init(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    g_mutex_init(&self->mutex);
 | 
			
		||||
    self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 | 
			
		||||
        (GDestroyNotify) gbinder_remote_object_unref);
 | 
			
		||||
    self->watchers = g_ptr_array_new_with_free_func((GDestroyNotify)
 | 
			
		||||
        gbinder_client_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_class_init(
 | 
			
		||||
    TestServiceManagerHidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = test_servicemanager_hidl_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        test_servicemanager_hidl_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        test_servicemanager_hidl_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										69
									
								
								unit/common/test_servicemanager_hidl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								unit/common/test_servicemanager_hidl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TEST_SERVICEMANAGER_HIDL_H
 | 
			
		||||
#define TEST_SERVICEMANAGER_HIDL_H
 | 
			
		||||
 | 
			
		||||
#include <gbinder_types.h>
 | 
			
		||||
 | 
			
		||||
typedef struct test_servicemanager_hidl TestServiceManagerHidl;
 | 
			
		||||
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_hidl_new(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_free(
 | 
			
		||||
    TestServiceManagerHidl* sm);
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
test_servicemanager_hidl_ipc(
 | 
			
		||||
    TestServiceManagerHidl* self);
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_servicemanager_hidl_object_count(
 | 
			
		||||
    TestServiceManagerHidl* self);
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_hidl_lookup(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
#endif /* TEST_SERVICEMANAGER_HIDL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,12 +1,15 @@
 | 
			
		||||
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
#
 | 
			
		||||
# This script requires lcov, dirname
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
TESTS="\
 | 
			
		||||
unit_bridge \
 | 
			
		||||
unit_buffer \
 | 
			
		||||
unit_cleanup \
 | 
			
		||||
unit_client \
 | 
			
		||||
unit_config \
 | 
			
		||||
unit_driver \
 | 
			
		||||
unit_eventloop \
 | 
			
		||||
unit_ipc \
 | 
			
		||||
@@ -15,11 +18,15 @@ unit_local_reply \
 | 
			
		||||
unit_local_request \
 | 
			
		||||
unit_log \
 | 
			
		||||
unit_protocol \
 | 
			
		||||
unit_proxy_object \
 | 
			
		||||
unit_reader \
 | 
			
		||||
unit_remote_object \
 | 
			
		||||
unit_remote_reply \
 | 
			
		||||
unit_remote_request \
 | 
			
		||||
unit_servicemanager \
 | 
			
		||||
unit_servicemanager_aidl \
 | 
			
		||||
unit_servicemanager_aidl2 \
 | 
			
		||||
unit_servicemanager_hidl \
 | 
			
		||||
unit_servicename \
 | 
			
		||||
unit_servicepoll \
 | 
			
		||||
unit_writer"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								unit/unit_bridge/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								unit/unit_bridge/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_bridge
 | 
			
		||||
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										498
									
								
								unit/unit_bridge/unit_bridge.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										498
									
								
								unit/unit_bridge/unit_bridge.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,498 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "test_binder.h"
 | 
			
		||||
#include "test_servicemanager_hidl.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_client.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_bridge.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_proxy_object.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
#define SRC_DEV "/dev/srcbinder"
 | 
			
		||||
#define SRC_PRIV_DEV  SRC_DEV "-private"
 | 
			
		||||
#define DEST_DEV "/dev/dstbinder"
 | 
			
		||||
#define DEST_PRIV_DEV  DEST_DEV "-private"
 | 
			
		||||
#define TEST_IFACE "gbinder@1.0::ITest"
 | 
			
		||||
 | 
			
		||||
#define TX_CODE   GBINDER_FIRST_CALL_TRANSACTION
 | 
			
		||||
#define TX_PARAM  0x11111111
 | 
			
		||||
#define TX_RESULT 0x22222222
 | 
			
		||||
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-bridge-XXXXXX";
 | 
			
		||||
static const char* TEST_IFACES[] =  { TEST_IFACE, NULL };
 | 
			
		||||
static const char DEFAULT_CONFIG_DATA[] =
 | 
			
		||||
    "[Protocol]\n"
 | 
			
		||||
    "Default = hidl\n"
 | 
			
		||||
    "[ServiceManager]\n"
 | 
			
		||||
    "Default = hidl\n";
 | 
			
		||||
 | 
			
		||||
typedef struct test_config {
 | 
			
		||||
    char* dir;
 | 
			
		||||
    char* file;
 | 
			
		||||
} TestConfig;
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test object (registered with two GBinderIpc's)
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass TestLocalObjectClass;
 | 
			
		||||
typedef struct test_local_object {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GBinderIpc* ipc2;
 | 
			
		||||
} TestLocalObject;
 | 
			
		||||
G_DEFINE_TYPE(TestLocalObject, test_local_object, GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
#define TEST_TYPE_LOCAL_OBJECT test_local_object_get_type()
 | 
			
		||||
#define TEST_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        TEST_TYPE_LOCAL_OBJECT, TestLocalObject))
 | 
			
		||||
 | 
			
		||||
TestLocalObject*
 | 
			
		||||
test_local_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderIpc* ipc2,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestLocalObject* self = TEST_LOCAL_OBJECT
 | 
			
		||||
        (gbinder_local_object_new_with_type(TEST_TYPE_LOCAL_OBJECT,
 | 
			
		||||
            ipc, ifaces, txproc, user_data));
 | 
			
		||||
 | 
			
		||||
    self->ipc2 = gbinder_ipc_ref(ipc2);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc2, &self->parent);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_local_object_dispose(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    TestLocalObject* self = TEST_LOCAL_OBJECT(object);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_local_object_disposed(self->ipc2, &self->parent);
 | 
			
		||||
    G_OBJECT_CLASS(test_local_object_parent_class)->dispose(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_local_object_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_ipc_unref(TEST_LOCAL_OBJECT(object)->ipc2);
 | 
			
		||||
    G_OBJECT_CLASS(test_local_object_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_local_object_init(
 | 
			
		||||
    TestLocalObject* self)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_local_object_class_init(
 | 
			
		||||
    TestLocalObjectClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object_class = G_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object_class->dispose = test_local_object_dispose;
 | 
			
		||||
    object_class->finalize = test_local_object_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init(
 | 
			
		||||
    TestConfig* config,
 | 
			
		||||
    char* config_data)
 | 
			
		||||
{
 | 
			
		||||
    config->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    config->file = g_build_filename(config->dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(config->file, config_data ? config_data :
 | 
			
		||||
        DEFAULT_CONFIG_DATA, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_dir = config->dir;
 | 
			
		||||
    gbinder_config_file = config->file;
 | 
			
		||||
    GDEBUG("Wrote config to %s", config->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_deinit(
 | 
			
		||||
    TestConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    remove(config->file);
 | 
			
		||||
    g_free(config->file);
 | 
			
		||||
 | 
			
		||||
    remove(config->dir);
 | 
			
		||||
    g_free(config->dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_impl_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    TestServiceManagerHidl* sm = test_servicemanager_hidl_new(ipc);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm),
 | 
			
		||||
        GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return sm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const char* ifaces[] = { "foo", "bar", NULL};
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_bridge_new2(NULL, NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_bridge_new(NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_bridge_new("foo", NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_bridge_new("foo", ifaces, NULL, NULL));
 | 
			
		||||
    gbinder_bridge_free(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_basic {
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    TestServiceManagerHidl* src_impl;
 | 
			
		||||
    int src_notify_count;
 | 
			
		||||
    gboolean dest_name_added;
 | 
			
		||||
} TestBasic;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_basic_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    int* count = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 param = 0;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE));
 | 
			
		||||
    g_assert(code == TX_CODE);
 | 
			
		||||
 | 
			
		||||
    /* Make sure that parameter got delivered intact */
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, ¶m));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(param, == ,TX_PARAM);
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    (*count)++;
 | 
			
		||||
    GDEBUG("Got a request, replying");
 | 
			
		||||
    return gbinder_local_reply_append_int32
 | 
			
		||||
        (gbinder_local_object_new_reply(obj), TX_RESULT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestBasic* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Name added");
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_assert(!test->dest_name_added);
 | 
			
		||||
    test->dest_name_added = TRUE;
 | 
			
		||||
    if (test->src_notify_count) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_notify_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestBasic* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_assert(name);
 | 
			
		||||
    GDEBUG("'%s' is registered", name);
 | 
			
		||||
    g_assert_cmpint(test->src_notify_count, == ,0);
 | 
			
		||||
    test->src_notify_count++;
 | 
			
		||||
    /* Exit the loop after both things happen */
 | 
			
		||||
    if (test->dest_name_added) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_reply(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 result = 0;
 | 
			
		||||
 | 
			
		||||
    g_assert(reply);
 | 
			
		||||
    GDEBUG("Reply received");
 | 
			
		||||
 | 
			
		||||
    /* Make sure that result got delivered intact */
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, &result));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(result, == ,TX_RESULT);
 | 
			
		||||
 | 
			
		||||
    /* Exit the loop */
 | 
			
		||||
    g_main_loop_quit((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_death(
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("Source object died");
 | 
			
		||||
 | 
			
		||||
    /* Exit the loop */
 | 
			
		||||
    g_main_loop_quit((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    TestBasic test;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    TestServiceManagerHidl* dest_impl;
 | 
			
		||||
    GBinderServiceManager* src;
 | 
			
		||||
    GBinderServiceManager* dest;
 | 
			
		||||
    GBinderIpc* src_ipc;
 | 
			
		||||
    GBinderIpc* src_priv_ipc;
 | 
			
		||||
    GBinderIpc* dest_ipc;
 | 
			
		||||
    GBinderIpc* dest_priv_ipc;
 | 
			
		||||
    GBinderBridge* bridge;
 | 
			
		||||
    TestLocalObject* obj;
 | 
			
		||||
    GBinderRemoteObject* br_src_obj;
 | 
			
		||||
    GBinderRemoteObject* src_obj;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBinderClient* src_client;
 | 
			
		||||
    const char* name = "test";
 | 
			
		||||
    const char* fqname = TEST_IFACE "/test";
 | 
			
		||||
    int src_fd, dest_fd, h, n = 0;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    /* obj (DEST) <=> bridge <=> (SRC) mirror */
 | 
			
		||||
    src_ipc = gbinder_ipc_new(SRC_DEV);
 | 
			
		||||
    src_priv_ipc = gbinder_ipc_new(SRC_PRIV_DEV);
 | 
			
		||||
    dest_ipc = gbinder_ipc_new(DEST_DEV);
 | 
			
		||||
    dest_priv_ipc = gbinder_ipc_new(DEST_PRIV_DEV);
 | 
			
		||||
    test.src_impl = test_servicemanager_impl_new(SRC_PRIV_DEV);
 | 
			
		||||
    dest_impl = test_servicemanager_impl_new(DEST_PRIV_DEV);
 | 
			
		||||
    src_fd = gbinder_driver_fd(src_ipc->driver);
 | 
			
		||||
    dest_fd = gbinder_driver_fd(dest_ipc->driver);
 | 
			
		||||
    obj = test_local_object_new(dest_ipc, dest_priv_ipc, TEST_IFACES,
 | 
			
		||||
        test_basic_cb, &n);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_set_passthrough(src_fd, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(dest_fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(src_fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(dest_fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    src = gbinder_servicemanager_new(SRC_DEV);
 | 
			
		||||
    dest = gbinder_servicemanager_new(DEST_DEV);
 | 
			
		||||
 | 
			
		||||
    /* Both src and dest are required */
 | 
			
		||||
    g_assert(!gbinder_bridge_new(name, TEST_IFACES, src, NULL));
 | 
			
		||||
    bridge = gbinder_bridge_new2(NULL, name, TEST_IFACES, src, dest);
 | 
			
		||||
 | 
			
		||||
    /* Start watching the name */
 | 
			
		||||
    id = gbinder_servicemanager_add_registration_handler(src, fqname,
 | 
			
		||||
        test_basic_notify_cb, &test);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    /* Register the object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(dest, name, &obj->parent,
 | 
			
		||||
        test_basic_add_cb, &test));
 | 
			
		||||
 | 
			
		||||
    /* This loop quits after the name is added and notification is received */
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Bridge object has been registered on source");
 | 
			
		||||
    g_assert_cmpint(test.src_notify_count, == ,1);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(src, id);
 | 
			
		||||
 | 
			
		||||
    /* Get a remote reference to the object created by the bridge */
 | 
			
		||||
    br_src_obj = gbinder_servicemanager_get_service_sync(src, fqname, NULL);
 | 
			
		||||
    g_assert(gbinder_remote_object_ref(br_src_obj)); /* autoreleased */
 | 
			
		||||
    g_assert(!br_src_obj->dead);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * This is a trick specific to test_binder simulation. We need to
 | 
			
		||||
     * associate src_obj with the other side of the socket, so that the
 | 
			
		||||
     * call goes like this:
 | 
			
		||||
     *
 | 
			
		||||
     * src_obj (src_priv) => (src) bridge (dest) => (dest_priv) => obj
 | 
			
		||||
     *
 | 
			
		||||
     * Note that the original src_obj gets autoreleased and doesn't need
 | 
			
		||||
     * to be explicitly unreferenced.
 | 
			
		||||
     */
 | 
			
		||||
    src_obj = gbinder_remote_object_new(src_priv_ipc,
 | 
			
		||||
        br_src_obj->handle, REMOTE_OBJECT_CREATE_ALIVE);
 | 
			
		||||
 | 
			
		||||
    /* Make a call */
 | 
			
		||||
    GDEBUG("Submitting a call");
 | 
			
		||||
    src_client = gbinder_client_new(src_obj, TEST_IFACE);
 | 
			
		||||
    req = gbinder_client_new_request(src_client);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM);
 | 
			
		||||
    g_assert(gbinder_client_transact(src_client, TX_CODE, 0, req,
 | 
			
		||||
        test_basic_reply, NULL, test.loop));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    /* Wait for completion */
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Kill the destination object and wait for auto-created object to die */
 | 
			
		||||
    g_assert(!br_src_obj->dead);
 | 
			
		||||
    id = gbinder_remote_object_add_death_handler(br_src_obj, test_basic_death,
 | 
			
		||||
        test.loop);
 | 
			
		||||
    h = test_binder_handle(dest_fd, &obj->parent);
 | 
			
		||||
    g_assert_cmpint(h, > ,0); /* Zero is servicemanager */
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Killing destination object, handle %d", h);
 | 
			
		||||
    gbinder_local_object_drop(&obj->parent);
 | 
			
		||||
    test_binder_br_dead_binder(dest_fd, h);
 | 
			
		||||
 | 
			
		||||
    /* Wait for the auto-created object to die */
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
    g_assert(br_src_obj->dead);
 | 
			
		||||
    gbinder_remote_object_remove_handler(br_src_obj, id);
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Done");
 | 
			
		||||
 | 
			
		||||
    gbinder_bridge_free(bridge);
 | 
			
		||||
    gbinder_remote_object_unref(src_obj);
 | 
			
		||||
    gbinder_remote_object_unref(br_src_obj);
 | 
			
		||||
    test_servicemanager_hidl_free(test.src_impl);
 | 
			
		||||
    test_servicemanager_hidl_free(dest_impl);
 | 
			
		||||
    gbinder_servicemanager_unref(src);
 | 
			
		||||
    gbinder_servicemanager_unref(dest);
 | 
			
		||||
    gbinder_client_unref(src_client);
 | 
			
		||||
    test_binder_unregister_objects(src_fd);
 | 
			
		||||
    test_binder_unregister_objects(dest_fd);
 | 
			
		||||
    gbinder_ipc_unref(src_ipc);
 | 
			
		||||
    gbinder_ipc_unref(src_priv_ipc);
 | 
			
		||||
    gbinder_ipc_unref(dest_ipc);
 | 
			
		||||
    gbinder_ipc_unref(dest_priv_ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_basic_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/bridge/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -68,15 +68,47 @@ test_null(
 | 
			
		||||
    gbinder_buffer_free(buf2);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(NULL);
 | 
			
		||||
    gbinder_buffer_contents_list_free(NULL);
 | 
			
		||||
    g_assert(!gbinder_buffer_driver(NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_objects(NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_io(NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_data(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_data(NULL, &size));
 | 
			
		||||
    g_assert(!gbinder_buffer_contents(NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_contents_list_add(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_buffer_contents_list_dup(NULL));
 | 
			
		||||
    g_assert(!size);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
 | 
			
		||||
    void* ptr = g_memdup(data, sizeof(data));
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(driver, ptr, sizeof(data), NULL);
 | 
			
		||||
    GBinderBufferContents* contents = gbinder_buffer_contents(buf);
 | 
			
		||||
    GBinderBufferContentsList* list = gbinder_buffer_contents_list_add
 | 
			
		||||
        (NULL, contents);
 | 
			
		||||
    GBinderBufferContentsList* list2 = gbinder_buffer_contents_list_dup(list);
 | 
			
		||||
 | 
			
		||||
    g_assert(contents);
 | 
			
		||||
    g_assert(list);
 | 
			
		||||
    g_assert(list2);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    gbinder_buffer_contents_list_free(list);
 | 
			
		||||
    gbinder_buffer_contents_list_free(list2);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * parent
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -112,12 +144,14 @@ test_parent(
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/buffer/"
 | 
			
		||||
#define TEST_(t) TEST_PREFIX t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "null", test_null);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "parent", test_parent);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    g_test_add_func(TEST_("parent"), test_parent);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -51,12 +51,12 @@ static TestOpt test_opt;
 | 
			
		||||
static
 | 
			
		||||
GBinderClient*
 | 
			
		||||
test_client_new(
 | 
			
		||||
    guint handle,
 | 
			
		||||
    guint h,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, handle);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE);
 | 
			
		||||
    GBinderClient* client = gbinder_client_new(obj, iface);
 | 
			
		||||
 | 
			
		||||
    g_assert(client);
 | 
			
		||||
@@ -97,9 +97,9 @@ void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE);
 | 
			
		||||
    const char* iface = "foo";
 | 
			
		||||
    GBinderClient* client = gbinder_client_new(obj, iface);
 | 
			
		||||
 | 
			
		||||
@@ -123,9 +123,9 @@ void
 | 
			
		||||
test_interfaces(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE);
 | 
			
		||||
    static const GBinderClientIfaceInfo ifaces[] = {
 | 
			
		||||
        {"33", 33 }, { "11", 11 }, { "22", 22 }
 | 
			
		||||
    };
 | 
			
		||||
@@ -198,7 +198,7 @@ test_dead(
 | 
			
		||||
    gbinder_remote_object_add_death_handler(obj, test_dead_done, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_dead_binder(fd, handle);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_assert(gbinder_remote_object_is_dead(obj));
 | 
			
		||||
 | 
			
		||||
@@ -207,6 +207,8 @@ test_dead(
 | 
			
		||||
    g_assert(!gbinder_client_transact(client, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_config/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_config/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_config
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										505
									
								
								unit/unit_config/unit_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								unit/unit_config/unit_config.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,505 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-config-XXXXXX";
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
test_value(
 | 
			
		||||
    GKeyFile* keyfile,
 | 
			
		||||
    const char* group,
 | 
			
		||||
    const char* key,
 | 
			
		||||
    GString* buf)
 | 
			
		||||
{
 | 
			
		||||
    char* value = g_key_file_get_value(keyfile, group, key, NULL);
 | 
			
		||||
 | 
			
		||||
    g_string_set_size(buf, 0);
 | 
			
		||||
    if (value) {
 | 
			
		||||
        g_string_append(buf, value);
 | 
			
		||||
        g_free(value);
 | 
			
		||||
        return buf->str;
 | 
			
		||||
    } else {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_keyfiles_equal(
 | 
			
		||||
    GKeyFile* keyfile1,
 | 
			
		||||
    GKeyFile* keyfile2)
 | 
			
		||||
{
 | 
			
		||||
    gboolean equal = FALSE;
 | 
			
		||||
    gsize ngroups;
 | 
			
		||||
    char** groups = g_key_file_get_groups(keyfile1, &ngroups);
 | 
			
		||||
    char** groups2 = g_key_file_get_groups(keyfile2, NULL);
 | 
			
		||||
 | 
			
		||||
    gutil_strv_sort(groups, TRUE);
 | 
			
		||||
    gutil_strv_sort(groups2, TRUE);
 | 
			
		||||
    if (gutil_strv_equal(groups, groups2)) {
 | 
			
		||||
        gsize i;
 | 
			
		||||
 | 
			
		||||
        equal = TRUE;
 | 
			
		||||
        for (i = 0; i < ngroups && equal; i++) {
 | 
			
		||||
            const char* group = groups[i];
 | 
			
		||||
            gsize nkeys;
 | 
			
		||||
            char** keys = g_key_file_get_keys(keyfile1, group, &nkeys, NULL);
 | 
			
		||||
            char** keys2 = g_key_file_get_keys(keyfile2, group, &nkeys, NULL);
 | 
			
		||||
 | 
			
		||||
            equal = FALSE;
 | 
			
		||||
            gutil_strv_sort(keys, TRUE);
 | 
			
		||||
            gutil_strv_sort(keys2, TRUE);
 | 
			
		||||
            if (gutil_strv_equal(keys, keys2)) {
 | 
			
		||||
                gsize k;
 | 
			
		||||
 | 
			
		||||
                equal = TRUE;
 | 
			
		||||
                for (k = 0; k < nkeys && equal; k++) {
 | 
			
		||||
                    const char* key = keys[k];
 | 
			
		||||
                    char* v1 = g_key_file_get_value(keyfile1, group, key, NULL);
 | 
			
		||||
                    char* v2 = g_key_file_get_value(keyfile2, group, key, NULL);
 | 
			
		||||
 | 
			
		||||
                    if (g_strcmp0(v1, v2)) {
 | 
			
		||||
                        equal = FALSE;
 | 
			
		||||
                        GDEBUG("Values for %s/%s don't match ('%s' vs '%s')",
 | 
			
		||||
                            group, key, v1, v2);
 | 
			
		||||
                    }
 | 
			
		||||
                    g_free(v1);
 | 
			
		||||
                    g_free(v2);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Keys for %s don't match", group);
 | 
			
		||||
            }
 | 
			
		||||
            g_strfreev(keys);
 | 
			
		||||
            g_strfreev(keys2);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Groups don't match");
 | 
			
		||||
    }
 | 
			
		||||
    g_strfreev(groups);
 | 
			
		||||
    g_strfreev(groups2);
 | 
			
		||||
    if (!equal) {
 | 
			
		||||
        char* data1 = g_key_file_to_data(keyfile1, NULL, NULL);
 | 
			
		||||
        char* data2 = g_key_file_to_data(keyfile2, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("This:");
 | 
			
		||||
        GDEBUG("%s", data1);
 | 
			
		||||
        GDEBUG("Doesn't match this:");
 | 
			
		||||
        GDEBUG("%s", data2);
 | 
			
		||||
        g_free(data1);
 | 
			
		||||
        g_free(data2);
 | 
			
		||||
    }
 | 
			
		||||
    return equal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* default_name = gbinder_config_file;
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = NULL;
 | 
			
		||||
    gbinder_config_dir = NULL;
 | 
			
		||||
    g_assert(!gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    /* Reset the state again */
 | 
			
		||||
    gbinder_config_file = default_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * non_exist
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_non_exit(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* default_name = gbinder_config_file;
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    gbinder_config_file = file;
 | 
			
		||||
    g_assert(!gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    /* Reset the state again */
 | 
			
		||||
    gbinder_config_file = default_name;
 | 
			
		||||
 | 
			
		||||
    g_free(file);
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * bad_config
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_bad_config(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* default_name = gbinder_config_file;
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
    static const char garbage[] = "foo";
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    /* Try to load the garbage */
 | 
			
		||||
    g_assert(g_file_set_contents(file, garbage, -1, NULL));
 | 
			
		||||
    gbinder_config_file = file;
 | 
			
		||||
    g_assert(!gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    /* Reset the state again */
 | 
			
		||||
    gbinder_config_file = default_name;
 | 
			
		||||
 | 
			
		||||
    remove(file);
 | 
			
		||||
    g_free(file);
 | 
			
		||||
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * dirs
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_dirs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GKeyFile* k;
 | 
			
		||||
    GString* b = g_string_new(NULL);
 | 
			
		||||
    const char* default_file = gbinder_config_file;
 | 
			
		||||
    const char* default_dir = gbinder_config_dir;
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* subdir = g_build_filename(dir, "d", NULL);
 | 
			
		||||
    char* notafile = g_build_filename(subdir, "dir.conf", NULL);
 | 
			
		||||
    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* random_file = g_build_filename(subdir, "foo", NULL);
 | 
			
		||||
    static const char garbage[] = "foo";
 | 
			
		||||
    static const char config[] =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder = aidl\n"
 | 
			
		||||
        "/dev/hbinder = hidl\n";
 | 
			
		||||
    static const char config1[] =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/hwbinder = hidl\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl\n";
 | 
			
		||||
    static const char config2[] =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\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(random_file, garbage, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = file;
 | 
			
		||||
    gbinder_config_dir = subdir;
 | 
			
		||||
 | 
			
		||||
    /* Load the config */
 | 
			
		||||
    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/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");
 | 
			
		||||
 | 
			
		||||
    /* Remove the default file and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    g_assert_cmpint(remove(file), == ,0);
 | 
			
		||||
    k = gbinder_config_get();
 | 
			
		||||
    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/hwbinder",b), == ,"hidl");
 | 
			
		||||
    g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
 | 
			
		||||
    /* Damage one of the files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    g_assert(g_file_set_contents(file1, garbage, -1, NULL));
 | 
			
		||||
    k = gbinder_config_get();
 | 
			
		||||
    g_assert(k);
 | 
			
		||||
    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,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
 | 
			
		||||
    /* Disallow access to one of the files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    g_assert_cmpint(chmod(file1, 0), == ,0);
 | 
			
		||||
    k = gbinder_config_get();
 | 
			
		||||
    g_assert(k);
 | 
			
		||||
    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,"ServiceManager","/dev/binder",b),==,"aidl2");
 | 
			
		||||
 | 
			
		||||
    /* Delete the remaining files and try again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    g_assert_cmpint(remove(file1), == ,0);
 | 
			
		||||
    g_assert_cmpint(remove(file2), == ,0);
 | 
			
		||||
    g_assert(!gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    /* Undo all the damage */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = default_file;
 | 
			
		||||
    gbinder_config_dir = default_dir;
 | 
			
		||||
 | 
			
		||||
    remove(random_file);
 | 
			
		||||
    g_free(file);
 | 
			
		||||
    g_free(file1);
 | 
			
		||||
    g_free(file2);
 | 
			
		||||
    g_free(random_file);
 | 
			
		||||
 | 
			
		||||
    remove(notafile);
 | 
			
		||||
    remove(subdir);
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(notafile);
 | 
			
		||||
    g_free(subdir);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
    g_string_free(b, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * autorelease
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_autorelease(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* default_file = gbinder_config_file;
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GKeyFile* keyfile;
 | 
			
		||||
    static const char config[] = "[Protocol]";
 | 
			
		||||
 | 
			
		||||
    gbinder_config_exit(); /* Reset the state */
 | 
			
		||||
 | 
			
		||||
    /* Load the file */
 | 
			
		||||
    g_assert(g_file_set_contents(file, config, -1, NULL));
 | 
			
		||||
    gbinder_config_file = file;
 | 
			
		||||
    keyfile = gbinder_config_get();
 | 
			
		||||
    g_assert(keyfile);
 | 
			
		||||
 | 
			
		||||
    /* Second call returns the same pointer */
 | 
			
		||||
    g_assert(keyfile == gbinder_config_get());
 | 
			
		||||
 | 
			
		||||
    test_quit_later_n(loop, 2);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
 | 
			
		||||
    /* Reset the state again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = default_file;
 | 
			
		||||
 | 
			
		||||
    remove(file);
 | 
			
		||||
    g_free(file);
 | 
			
		||||
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Presets
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_presets_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const char* in;
 | 
			
		||||
    const char* out;
 | 
			
		||||
} TestPresetsData;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_presets(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestPresetsData* test = test_data;
 | 
			
		||||
    const char* default_file = gbinder_config_file;
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
    GKeyFile* expected = g_key_file_new();
 | 
			
		||||
    GKeyFile* keyfile;
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    /* Load the file */
 | 
			
		||||
    if (test->in) {
 | 
			
		||||
        g_assert(g_file_set_contents(file, test->in, -1, NULL));
 | 
			
		||||
        gbinder_config_file = file;
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_config_file = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    keyfile = gbinder_config_get();
 | 
			
		||||
    g_assert(keyfile);
 | 
			
		||||
 | 
			
		||||
    /* Compare it against the expected value */
 | 
			
		||||
    g_assert(g_key_file_load_from_data(expected, test->out, (gsize)-1,
 | 
			
		||||
        G_KEY_FILE_NONE, NULL));
 | 
			
		||||
    g_assert(test_keyfiles_equal(keyfile, expected));
 | 
			
		||||
 | 
			
		||||
    /* Reset the state again */
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = default_file;
 | 
			
		||||
 | 
			
		||||
    remove(file);
 | 
			
		||||
    g_free(file);
 | 
			
		||||
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
    g_key_file_unref(expected);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TestPresetsData test_presets_data [] = {
 | 
			
		||||
    {
 | 
			
		||||
        "override",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 28\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/vndbinder = aidl\n",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 28\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "/dev/vndbinder = aidl\n" /* Preset is overridden */
 | 
			
		||||
    },{
 | 
			
		||||
        "too_small",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 27\n",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 27\n"
 | 
			
		||||
    },{
 | 
			
		||||
       "28",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 28",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 28\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "/dev/vndbinder = aidl2\n"
 | 
			
		||||
    },{
 | 
			
		||||
        "29",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 29",
 | 
			
		||||
 | 
			
		||||
        "[General]\n"
 | 
			
		||||
        "ApiLevel = 29\n"
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "/dev/vndbinder = aidl2\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "/dev/vndbinder = aidl2\n"
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/config/"
 | 
			
		||||
#define TEST_(t) TEST_PREFIX t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    guint i;
 | 
			
		||||
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("non_exist"), test_non_exit);
 | 
			
		||||
    g_test_add_func(TEST_("dirs"), test_dirs);
 | 
			
		||||
    g_test_add_func(TEST_("bad_config"), test_bad_config);
 | 
			
		||||
    g_test_add_func(TEST_("autorelease"), test_autorelease);
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_presets_data); i++) {
 | 
			
		||||
        const TestPresetsData* test = test_presets_data + i;
 | 
			
		||||
        char* path;
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_("presets/"), test->name, NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_presets);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
#include "gbinder_handler.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +61,9 @@ test_basic(
 | 
			
		||||
    driver = gbinder_driver_new(dev, NULL);
 | 
			
		||||
    g_assert(driver);
 | 
			
		||||
    g_assert(!g_strcmp0(dev, gbinder_driver_dev(driver)));
 | 
			
		||||
    g_assert(gbinder_driver_protocol(driver));
 | 
			
		||||
    g_assert(gbinder_driver_protocol(driver) ==
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev));
 | 
			
		||||
    g_assert(gbinder_driver_ref(driver) == driver);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
    gbinder_driver_free_buffer(driver, NULL);
 | 
			
		||||
@@ -72,9 +76,11 @@ test_basic(
 | 
			
		||||
    g_assert(gbinder_driver_exit_looper(driver));
 | 
			
		||||
    g_assert(!gbinder_driver_request_death_notification(driver, NULL));
 | 
			
		||||
    g_assert(!gbinder_driver_clear_death_notification(driver, NULL));
 | 
			
		||||
    g_assert(!gbinder_driver_dead_binder_done(NULL, NULL));
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_handler_transact(NULL, NULL, NULL, 0, 0, NULL));
 | 
			
		||||
    g_assert(!gbinder_handler_can_loop(NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2020-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -230,6 +230,8 @@ test_callback(
 | 
			
		||||
 | 
			
		||||
    gbinder_eventloop_set(NULL);
 | 
			
		||||
    cb = gbinder_idle_callback_new(test_quit_cb, loop, NULL);
 | 
			
		||||
    g_assert(gbinder_idle_callback_ref(cb) == cb);
 | 
			
		||||
    gbinder_idle_callback_unref(cb);
 | 
			
		||||
    gbinder_idle_callback_schedule(cb);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    gbinder_idle_callback_unref(cb);
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -84,10 +84,16 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_ref(null));
 | 
			
		||||
    gbinder_ipc_unref(null);
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert(status == (-EINVAL));
 | 
			
		||||
    g_assert(gbinder_ipc_transact_sync_oneway(null, 0, 0, NULL) == (-EINVAL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,-EINVAL);
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,-EINVAL);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(null, 0, 0, NULL), == ,
 | 
			
		||||
        -EINVAL);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_worker.sync_oneway(null, 0, 0, NULL), == ,
 | 
			
		||||
        -EINVAL);
 | 
			
		||||
    g_assert(!gbinder_ipc_transact(null, 0, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_custom(null, NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_object_registry(null));
 | 
			
		||||
@@ -97,20 +103,30 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_object_registry_ref(NULL));
 | 
			
		||||
    gbinder_object_registry_unref(NULL);
 | 
			
		||||
    g_assert(!gbinder_object_registry_get_local(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_object_registry_get_remote(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_object_registry_get_remote(NULL, 0, FALSE));
 | 
			
		||||
    g_assert(!gbinder_ipc_find_local_object(NULL, NULL, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_basic_find_none(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
 | 
			
		||||
    g_assert(ipc);
 | 
			
		||||
    g_assert(ipc2);
 | 
			
		||||
@@ -118,17 +134,21 @@ test_basic(
 | 
			
		||||
    gbinder_ipc_cancel(ipc2, 0); /* not a valid transaction */
 | 
			
		||||
    gbinder_ipc_unref(ipc2);
 | 
			
		||||
 | 
			
		||||
    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));
 | 
			
		||||
 | 
			
		||||
    /* Second gbinder_ipc_new returns the same (default) object */
 | 
			
		||||
    g_assert(gbinder_ipc_new(NULL, NULL) == ipc);
 | 
			
		||||
    g_assert(gbinder_ipc_new("", NULL) == ipc);
 | 
			
		||||
    g_assert(gbinder_ipc_new(NULL) == ipc);
 | 
			
		||||
    g_assert(gbinder_ipc_new("") == ipc);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* Invalid path */
 | 
			
		||||
    g_assert(!gbinder_ipc_new("invalid path", NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_new("invalid path"));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -153,7 +173,7 @@ void
 | 
			
		||||
test_async_oneway(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -180,17 +200,17 @@ void
 | 
			
		||||
test_sync_oneway(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    g_assert(gbinder_ipc_transact_sync_oneway(ipc, 0, 1, req) ==
 | 
			
		||||
        GBINDER_STATUS_OK);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -202,7 +222,7 @@ void
 | 
			
		||||
test_sync_reply_ok_status(
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -223,7 +243,7 @@ test_sync_reply_ok_status(
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code, data->bytes);
 | 
			
		||||
 | 
			
		||||
    tx_reply = gbinder_ipc_transact_sync_reply(ipc, handle, code, req, status);
 | 
			
		||||
    tx_reply = gbinder_ipc_sync_main.sync_reply(ipc, handle, code, req, status);
 | 
			
		||||
    g_assert(tx_reply);
 | 
			
		||||
 | 
			
		||||
    result_out = gbinder_remote_reply_read_string16(tx_reply);
 | 
			
		||||
@@ -235,6 +255,7 @@ test_sync_reply_ok_status(
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -258,13 +279,14 @@ void
 | 
			
		||||
test_sync_reply_error(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    const guint32 handle = 0;
 | 
			
		||||
    const guint32 code = 1;
 | 
			
		||||
    const gint expected_status = GBINDER_STATUS_FAILED;
 | 
			
		||||
    const gint expected_status = (-EINVAL);
 | 
			
		||||
    const gint unexpected_status = GBINDER_STATUS_FAILED;
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
@@ -272,12 +294,22 @@ test_sync_reply_error(
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply_status(fd, expected_status);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(ipc, handle, code, req, &status));
 | 
			
		||||
    g_assert(status == expected_status);
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
 | 
			
		||||
    g_assert_cmpint(status, == ,expected_status);
 | 
			
		||||
 | 
			
		||||
    /* GBINDER_STATUS_FAILED gets replaced with -EFAULT */
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply_status(fd, unexpected_status);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
 | 
			
		||||
    g_assert_cmpint(status, == ,-EFAULT);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -316,7 +348,7 @@ void
 | 
			
		||||
test_transact_ok(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -348,6 +380,7 @@ test_transact_ok(
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -374,7 +407,7 @@ void
 | 
			
		||||
test_transact_dead(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -395,6 +428,7 @@ test_transact_dead(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -421,7 +455,7 @@ void
 | 
			
		||||
test_transact_failed(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -442,6 +476,7 @@ test_transact_failed(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -470,7 +505,7 @@ void
 | 
			
		||||
test_transact_status(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
@@ -491,6 +526,7 @@ test_transact_status(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -512,7 +548,7 @@ void
 | 
			
		||||
test_transact_custom(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id = gbinder_ipc_transact_custom(ipc, NULL,
 | 
			
		||||
        test_transact_custom_done, NULL, loop);
 | 
			
		||||
@@ -522,6 +558,7 @@ test_transact_custom(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -543,7 +580,7 @@ void
 | 
			
		||||
test_transact_custom2(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id = gbinder_ipc_transact_custom(ipc, NULL, NULL,
 | 
			
		||||
        test_transact_custom_destroy, loop);
 | 
			
		||||
@@ -553,6 +590,7 @@ test_transact_custom2(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -575,7 +613,7 @@ void
 | 
			
		||||
test_transact_custom3(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */
 | 
			
		||||
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_custom3_exec,
 | 
			
		||||
@@ -586,6 +624,7 @@ test_transact_custom3(
 | 
			
		||||
 | 
			
		||||
    /* Reference to GBinderIpc is released by test_transact_custom3_exec */
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -624,7 +663,7 @@ void
 | 
			
		||||
test_transact_cancel(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel_exec,
 | 
			
		||||
        test_transact_cancel_done, test_transact_cancel_destroy, loop);
 | 
			
		||||
@@ -635,6 +674,7 @@ test_transact_cancel(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -669,7 +709,7 @@ void
 | 
			
		||||
test_transact_cancel2(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */
 | 
			
		||||
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel2_exec,
 | 
			
		||||
@@ -680,9 +720,151 @@ test_transact_cancel2(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_2way
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_transact_2way_incoming_proc(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    int* incoming_call = user_data;
 | 
			
		||||
 | 
			
		||||
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    g_assert_cmpuint(flags, == ,0);
 | 
			
		||||
    g_assert_cmpint(gbinder_remote_request_sender_pid(req), == ,getpid());
 | 
			
		||||
    g_assert_cmpint(gbinder_remote_request_sender_euid(req), == ,geteuid());
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,"test");
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_read_string8(req), == ,"message");
 | 
			
		||||
    g_assert_cmpuint(code, == ,2);
 | 
			
		||||
    g_assert_cmpint(*incoming_call, == ,0);
 | 
			
		||||
    (*incoming_call)++;
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return gbinder_local_object_new_reply(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_2way(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    const char* const ifaces[] = { "test", NULL };
 | 
			
		||||
    const guint32 handle = 0;
 | 
			
		||||
    const guint32 code = 1;
 | 
			
		||||
    int incoming_call = 0;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_2way_incoming_proc, &incoming_call);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderLocalRequest* incoming_req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    /* Prepare reply */
 | 
			
		||||
    g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR));
 | 
			
		||||
 | 
			
		||||
    /* Prepare incoming request */
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    prot->write_rpc_header(&writer, "test");
 | 
			
		||||
    gbinder_writer_append_string8(&writer, "message");
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 2,
 | 
			
		||||
        gbinder_local_request_data(req)->bytes);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code,
 | 
			
		||||
        gbinder_local_reply_data(reply)->bytes);
 | 
			
		||||
 | 
			
		||||
    /* NB. Reusing test_transact_ok_done and test_transact_ok_destroy */
 | 
			
		||||
    g_assert(gbinder_ipc_transact(ipc, handle, code, 0, req,
 | 
			
		||||
        test_transact_ok_done, test_transact_ok_destroy, loop));
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_local_request_unref(incoming_req);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_unhandled
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_unhandled_done(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!reply);
 | 
			
		||||
    g_assert_cmpint(status, == ,GBINDER_STATUS_DEAD_OBJECT);
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_unhandled_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver);
 | 
			
		||||
    int fd = gbinder_driver_fd(driver);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_ipc_transact(ipc, 1 /* Non-existent object */,
 | 
			
		||||
        gbinder_driver_protocol(driver)->ping_tx, 0, req,
 | 
			
		||||
        test_transact_unhandled_done, NULL, loop));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_unhandled(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_transact_unhandled_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_incoming
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -712,10 +894,10 @@ test_transact_incoming_proc(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_incoming(
 | 
			
		||||
test_transact_incoming_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
			
		||||
@@ -724,31 +906,48 @@ test_transact_incoming(
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_incoming_proc, loop);
 | 
			
		||||
    GBinderLocalRequest* ping = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(ping, &writer);
 | 
			
		||||
    prot->write_ping(&writer);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    prot->write_rpc_header(&writer, "test");
 | 
			
		||||
    gbinder_writer_append_string8(&writer, "message");
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1, data->bytes);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_br_transaction(fd, obj, prot->ping_tx,
 | 
			
		||||
        gbinder_local_request_data(ping)->bytes);
 | 
			
		||||
    test_binder_br_transaction_complete(fd); /* For reply */
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1,
 | 
			
		||||
        gbinder_local_request_data(req)->bytes);
 | 
			
		||||
    test_binder_br_transaction_complete(fd); /* For reply */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_request_unref(ping);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_incoming(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_transact_incoming_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_status_reply
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -776,10 +975,10 @@ test_transact_status_reply_proc(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_status_reply(
 | 
			
		||||
test_transact_status_reply_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
			
		||||
@@ -798,7 +997,8 @@ test_transact_status_reply(
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1, data->bytes);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_br_transaction_complete(fd); /* For reply */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
			
		||||
@@ -810,9 +1010,18 @@ test_transact_status_reply(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_status_reply(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_transact_status_reply_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_async
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -883,10 +1092,10 @@ test_transact_async_proc(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async(
 | 
			
		||||
test_transact_async_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
			
		||||
@@ -905,7 +1114,8 @@ test_transact_async(
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1, data->bytes);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_br_transaction_complete(fd); /* For reply */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
			
		||||
@@ -917,9 +1127,18 @@ test_transact_async(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_transact_async_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_async_sync
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -956,10 +1175,10 @@ test_transact_async_sync_proc(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async_sync(
 | 
			
		||||
test_transact_async_sync_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
			
		||||
@@ -978,7 +1197,8 @@ test_transact_async_sync(
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1, data->bytes);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_br_transaction_complete(fd); /* For reply */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE_ONE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
			
		||||
@@ -990,9 +1210,18 @@ test_transact_async_sync(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async_sync(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_transact_async_sync_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * drop_remote_refs
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -1010,10 +1239,10 @@ test_drop_remote_refs_cb(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_drop_remote_refs(
 | 
			
		||||
test_drop_remote_refs_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, NULL, NULL, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
@@ -1022,7 +1251,7 @@ test_drop_remote_refs(
 | 
			
		||||
        test_drop_remote_refs_cb, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_acquire(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->strong_refs == 1);
 | 
			
		||||
@@ -1032,9 +1261,18 @@ test_drop_remote_refs(
 | 
			
		||||
    /* gbinder_ipc_exit will drop the remote reference */
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_drop_remote_refs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_drop_remote_refs_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * cancel_on_exit
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -1055,7 +1293,7 @@ void
 | 
			
		||||
test_cancel_on_exit(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
@@ -1069,6 +1307,7 @@ test_cancel_on_exit(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1097,7 +1336,9 @@ int main(int argc, char* argv[])
 | 
			
		||||
    g_test_add_func(TEST_("transact_custom3"), test_transact_custom3);
 | 
			
		||||
    g_test_add_func(TEST_("transact_cancel"), test_transact_cancel);
 | 
			
		||||
    g_test_add_func(TEST_("transact_cancel2"), test_transact_cancel2);
 | 
			
		||||
    g_test_add_func(TEST_("transact_2way"), test_transact_2way);
 | 
			
		||||
    g_test_add_func(TEST_("transact_incoming"), test_transact_incoming);
 | 
			
		||||
    g_test_add_func(TEST_("transact_unhandled"), test_transact_unhandled);
 | 
			
		||||
    g_test_add_func(TEST_("transact_status_reply"), test_transact_status_reply);
 | 
			
		||||
    g_test_add_func(TEST_("transact_async"), test_transact_async);
 | 
			
		||||
    g_test_add_func(TEST_("transact_async_sync"), test_transact_async_sync);
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -137,7 +137,7 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_custom(NULL, NULL, NULL, NULL, NULL));
 | 
			
		||||
    gbinder_local_object_handle_increfs(NULL);
 | 
			
		||||
    gbinder_local_object_handle_decrefs(NULL);
 | 
			
		||||
    gbinder_local_object_handle_acquire(NULL);
 | 
			
		||||
    gbinder_local_object_handle_acquire(NULL, NULL);
 | 
			
		||||
    gbinder_local_object_handle_release(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +152,7 @@ test_basic(
 | 
			
		||||
{
 | 
			
		||||
    const char* const ifaces_foo[] = { "foo", NULL };
 | 
			
		||||
    const char* const ifaces_bar[] = { "bar", NULL };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderLocalObject* foo;
 | 
			
		||||
    GBinderLocalObject* bar;
 | 
			
		||||
@@ -179,7 +179,7 @@ test_basic(
 | 
			
		||||
        base_interface, -1) == GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED);
 | 
			
		||||
    gbinder_local_object_handle_increfs(foo);
 | 
			
		||||
    gbinder_local_object_handle_decrefs(foo);
 | 
			
		||||
    gbinder_local_object_handle_acquire(foo);
 | 
			
		||||
    gbinder_local_object_handle_acquire(foo, NULL);
 | 
			
		||||
    gbinder_local_object_handle_release(foo);
 | 
			
		||||
    gbinder_local_object_unref(foo);
 | 
			
		||||
 | 
			
		||||
@@ -201,7 +201,7 @@ test_ping(
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
@@ -247,7 +247,7 @@ test_interface(
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    const char* const ifaces[] = { "x", NULL };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
@@ -296,7 +296,7 @@ test_hidl_ping(
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
@@ -346,7 +346,7 @@ test_get_descriptor(
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
@@ -402,7 +402,7 @@ test_descriptor_chain(
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL };
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
@@ -476,7 +476,7 @@ test_custom_iface(
 | 
			
		||||
    int count = 0, status = INT_MAX;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces,
 | 
			
		||||
@@ -583,7 +583,7 @@ test_reply_status(
 | 
			
		||||
    int count = 0, status = 0;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces,
 | 
			
		||||
@@ -621,10 +621,10 @@ test_increfs_cb(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_increfs(
 | 
			
		||||
test_increfs_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, NULL, NULL, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
@@ -635,16 +635,25 @@ test_increfs(
 | 
			
		||||
    /* ipc is not an object, will be ignored */
 | 
			
		||||
    test_binder_br_increfs(fd, ipc);
 | 
			
		||||
    test_binder_br_increfs(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->weak_refs == 1);
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_increfs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_increfs_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * decrefs
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -663,10 +672,10 @@ test_decrefs_cb(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_decrefs(
 | 
			
		||||
test_decrefs_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, NULL, NULL, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
@@ -678,16 +687,25 @@ test_decrefs(
 | 
			
		||||
    test_binder_br_decrefs(fd, ipc);
 | 
			
		||||
    test_binder_br_increfs(fd, obj);
 | 
			
		||||
    test_binder_br_decrefs(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->weak_refs == 0);
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_decrefs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_decrefs_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * acquire
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -705,10 +723,10 @@ test_acquire_cb(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_acquire(
 | 
			
		||||
test_acquire_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, NULL, NULL, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
@@ -719,16 +737,25 @@ test_acquire(
 | 
			
		||||
    /* ipc is not an object, will be ignored */
 | 
			
		||||
    test_binder_br_acquire(fd, ipc);
 | 
			
		||||
    test_binder_br_acquire(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->strong_refs == 1);
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_acquire(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_acquire_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * release
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -747,10 +774,10 @@ test_release_cb(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_release(
 | 
			
		||||
test_release_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
@@ -761,16 +788,25 @@ test_release(
 | 
			
		||||
    test_binder_br_release(fd, ipc);
 | 
			
		||||
    test_binder_br_acquire(fd, obj);
 | 
			
		||||
    test_binder_br_release(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->strong_refs == 0);
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_release(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_release_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -88,7 +88,8 @@ test_null(
 | 
			
		||||
    gbinder_local_reply_init_writer(NULL, NULL);
 | 
			
		||||
    gbinder_local_reply_init_writer(NULL, &writer);
 | 
			
		||||
    g_assert(!gbinder_local_reply_data(NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_new_from_data(NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_contents(NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_set_contents(NULL, NULL, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_cleanup(NULL, NULL, &count);
 | 
			
		||||
    gbinder_local_reply_cleanup(NULL, test_int_inc, &count);
 | 
			
		||||
@@ -357,10 +358,10 @@ test_hidl_string(
 | 
			
		||||
    gbinder_local_reply_append_hidl_string(reply, NULL);
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->count == 2);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -380,10 +381,10 @@ test_hidl_string_vec(
 | 
			
		||||
    gbinder_local_reply_append_hidl_string_vec(reply, NULL, 0);
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->count == 2);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -399,7 +400,7 @@ test_local_object(
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(NULL);
 | 
			
		||||
    const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL };
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
@@ -479,7 +480,8 @@ test_remote_reply(
 | 
			
		||||
 | 
			
		||||
    /* Copy flat structures (no binder objects) */
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, bytes);
 | 
			
		||||
    req2 = gbinder_local_reply_new_from_data(buffer);
 | 
			
		||||
    req2 = gbinder_local_reply_new(io);
 | 
			
		||||
    g_assert(gbinder_local_reply_set_contents(req2, buffer, NULL) == req2);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_reply_data(req2);
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -94,7 +94,7 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_local_request_new(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_local_request_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_local_request_new_from_data(NULL));
 | 
			
		||||
    g_assert(!gbinder_local_request_new_from_data(NULL, NULL));
 | 
			
		||||
    gbinder_local_request_unref(NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(NULL, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(NULL, &writer);
 | 
			
		||||
@@ -385,10 +385,10 @@ test_hidl_string(
 | 
			
		||||
    gbinder_local_request_append_hidl_string(req, NULL);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->count == 2);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -408,10 +408,10 @@ test_hidl_string_vec(
 | 
			
		||||
    gbinder_local_request_append_hidl_string_vec(req, NULL, 0);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->count == 2);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -490,7 +490,7 @@ test_remote_request(
 | 
			
		||||
 | 
			
		||||
    /* Copy flat structures (no binder objects) */
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, bytes);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, NULL);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_request_data(req2);
 | 
			
		||||
@@ -503,7 +503,7 @@ test_remote_request(
 | 
			
		||||
 | 
			
		||||
    /* Same thing but with non-NULL (albeit empty) array of objects */
 | 
			
		||||
    buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, NULL);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_request_data(req2);
 | 
			
		||||
@@ -572,7 +572,7 @@ test_remote_request_obj(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, NULL);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    test_remote_request_obj_validate_data(gbinder_local_request_data(req2));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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
 | 
			
		||||
@@ -33,6 +33,7 @@
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
@@ -43,36 +44,118 @@
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
typedef struct test_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const char* prot;
 | 
			
		||||
    const char* dev;
 | 
			
		||||
} TestData;
 | 
			
		||||
 | 
			
		||||
typedef struct test_header_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const char* prot;
 | 
			
		||||
    const char* dev;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    const guint8* header;
 | 
			
		||||
    guint header_size;
 | 
			
		||||
} TestHeaderData;
 | 
			
		||||
 | 
			
		||||
static const guint8 test_header_binder [] = {
 | 
			
		||||
static const guint8 test_header_aidl [] = {
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_RPC_FLAGS),
 | 
			
		||||
    TEST_INT32_BYTES(3),
 | 
			
		||||
    TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'),
 | 
			
		||||
    TEST_INT16_BYTES('o'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 test_header_hwbinder [] = {
 | 
			
		||||
static const guint8 test_header_aidl2 [] = {
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_RPC_FLAGS),
 | 
			
		||||
    TEST_INT32_BYTES(UNSET_WORK_SOURCE),
 | 
			
		||||
    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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const TestHeaderData test_header_tests[] = {
 | 
			
		||||
    { "binder", GBINDER_DEFAULT_BINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_binder) },
 | 
			
		||||
    { "hwbinder", GBINDER_DEFAULT_HWBINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_hwbinder) }
 | 
			
		||||
    { "aidl/ok", "aidl", GBINDER_DEFAULT_BINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_aidl) },
 | 
			
		||||
    { "aidl/short", "aidl", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl, 8 }, /* Short packet */
 | 
			
		||||
    { "aidl2/ok", "aidl2", GBINDER_DEFAULT_BINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_aidl2) },
 | 
			
		||||
    { "aidl2/short/1", "aidl2", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl2, 1 }, /* Short packet */
 | 
			
		||||
    { "aidl2/short/2", "aidl2", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl2, 5 }, /* Short packet */
 | 
			
		||||
    { "aidl2/short/3", "adl2", GBINDER_DEFAULT_BINDER, NULL,
 | 
			
		||||
      test_header_aidl2, 9 }, /* Short packet */
 | 
			
		||||
    { "hidl/ok", "hidl", GBINDER_DEFAULT_HWBINDER, "foo",
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(test_header_hidl) },
 | 
			
		||||
    { "hidl/short", "hidl", GBINDER_DEFAULT_HWBINDER, NULL,
 | 
			
		||||
      test_header_hidl, 1 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct test_config {
 | 
			
		||||
    char* dir;
 | 
			
		||||
    char* file;
 | 
			
		||||
} TestConfig;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init(
 | 
			
		||||
    TestConfig* test,
 | 
			
		||||
    const char* config)
 | 
			
		||||
{
 | 
			
		||||
    test->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    test->file = g_build_filename(test->dir, "test.conf", NULL);
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_rpc_protocol_exit();
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    /* Write the config */
 | 
			
		||||
    g_assert(g_file_set_contents(test->file, config, -1, NULL));
 | 
			
		||||
    gbinder_config_file = test->file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init2(
 | 
			
		||||
    TestConfig* test,
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    const char* prot)
 | 
			
		||||
{
 | 
			
		||||
    char* config = g_strconcat("[Protocol]\n", dev, " = ", prot, "\n", NULL);
 | 
			
		||||
 | 
			
		||||
    test_config_init(test, config);
 | 
			
		||||
    g_free(config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_cleanup(
 | 
			
		||||
    TestConfig* test)
 | 
			
		||||
{
 | 
			
		||||
    /* Undo the damage */
 | 
			
		||||
    gbinder_rpc_protocol_exit();
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = NULL;
 | 
			
		||||
 | 
			
		||||
    remove(test->file);
 | 
			
		||||
    g_free(test->file);
 | 
			
		||||
 | 
			
		||||
    remove(test->dir);
 | 
			
		||||
    g_free(test->dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * device
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -82,12 +165,129 @@ void
 | 
			
		||||
test_device(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(gbinder_rpc_protocol_for_device(NULL) ==
 | 
			
		||||
        &gbinder_rpc_protocol_binder);
 | 
			
		||||
    g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER) ==
 | 
			
		||||
        &gbinder_rpc_protocol_binder);
 | 
			
		||||
    g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_HWBINDER) ==
 | 
			
		||||
        &gbinder_rpc_protocol_hwbinder);
 | 
			
		||||
    const GBinderRpcProtocol* p;
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(NULL);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * config1
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config1(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderRpcProtocol* p;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config,
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "/dev/binder = hidl\n" /* Redefined name for /dev/binder */
 | 
			
		||||
        "/dev/hwbinder = foo\n"); /* Invalid protocol name */
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(NULL);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/hwbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/binder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl"); /* Redefined by config */
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/someotherbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * config2
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config2(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderRpcProtocol* p;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config,
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "Default = hidl\n"
 | 
			
		||||
        "/dev/vndbinder = hidl\n"
 | 
			
		||||
        "/dev/hwbinder = foo\n"); /* Invalid protocol name */
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(NULL);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/vndbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/hwbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/binder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    /* The default is redefined */
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/someotherbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * config3
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config3(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderRpcProtocol* p;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config,
 | 
			
		||||
        "[Whatever]\n"
 | 
			
		||||
        "/dev/hwbinder = aidl\n"); /* Ignored, wrong section */
 | 
			
		||||
 | 
			
		||||
    /* Just the default config */
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(NULL);
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/hwbinder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"hidl");
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device("/dev/binder");
 | 
			
		||||
    g_assert(p);
 | 
			
		||||
    g_assert_cmpstr(p->name, == ,"aidl");
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -97,14 +297,21 @@ test_device(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_no_header1(
 | 
			
		||||
    void)
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER), 0, 0);
 | 
			
		||||
    const TestData* test = test_data;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init2(&config, test->dev, test->prot);
 | 
			
		||||
 | 
			
		||||
    req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device
 | 
			
		||||
        (GBINDER_DEFAULT_BINDER), 0, 0);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, NULL);
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -114,21 +321,35 @@ test_no_header1(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_no_header2(
 | 
			
		||||
    void)
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderRpcProtocol* p = &gbinder_rpc_protocol_binder;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, p);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, p, 0, 0);
 | 
			
		||||
    const TestData* test = test_data;
 | 
			
		||||
    const GBinderRpcProtocol* p;
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init2(&config, test->dev, test->prot);
 | 
			
		||||
 | 
			
		||||
    p = gbinder_rpc_protocol_for_device(test->dev);
 | 
			
		||||
    driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, p);
 | 
			
		||||
    req = gbinder_remote_request_new(NULL, p, 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_DUMP_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(TEST_ARRAY_AND_SIZE(test_header_binder)),
 | 
			
		||||
        sizeof(test_header_binder), NULL));
 | 
			
		||||
        g_memdup(TEST_ARRAY_AND_SIZE(test_header_aidl)),
 | 
			
		||||
        sizeof(test_header_aidl), NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TestData test_no_header_data[] = {
 | 
			
		||||
    { "aidl", "aidl", GBINDER_DEFAULT_BINDER },
 | 
			
		||||
    { "aidl2", "aidl2", GBINDER_DEFAULT_BINDER },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * write_header
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -139,17 +360,24 @@ test_write_header(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHeaderData* test = test_data;
 | 
			
		||||
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(test->dev);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    const GBinderRpcProtocol* prot;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init2(&config, test->dev, test->prot);
 | 
			
		||||
 | 
			
		||||
    prot = gbinder_rpc_protocol_for_device(test->dev);
 | 
			
		||||
    req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    prot->write_rpc_header(&writer, test->iface);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(data->bytes->len == test->header_size);
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, test->header, test->header_size));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -162,16 +390,23 @@ test_read_header(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHeaderData* test = test_data;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(test->dev, NULL);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(test->dev), 0, 0);
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
 | 
			
		||||
    test_config_init2(&config, test->dev, test->prot);
 | 
			
		||||
 | 
			
		||||
    driver = gbinder_driver_new(test->dev, NULL);
 | 
			
		||||
    req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device
 | 
			
		||||
        (test->dev), 0, 0);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, g_memdup(test->header, test->header_size),
 | 
			
		||||
        test->header_size, NULL));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), test->iface));
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,test->iface);
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
 | 
			
		||||
    test_config_cleanup(&config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -187,20 +422,36 @@ int main(int argc, char* argv[])
 | 
			
		||||
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("device"), test_device);
 | 
			
		||||
    g_test_add_func(TEST_("no_header1"), test_no_header1);
 | 
			
		||||
    g_test_add_func(TEST_("no_header2"), test_no_header2);
 | 
			
		||||
    g_test_add_func(TEST_("config1"), test_config1);
 | 
			
		||||
    g_test_add_func(TEST_("config2"), test_config2);
 | 
			
		||||
    g_test_add_func(TEST_("config3"), test_config3);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_no_header_data); i++) {
 | 
			
		||||
        const TestData* test = test_no_header_data + i;
 | 
			
		||||
        char* path;
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_("no_header1/"), test->name, NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_no_header1);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_("no_header2/"), test->name, NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_no_header2);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_header_tests); i++) {
 | 
			
		||||
        const TestHeaderData* test = test_header_tests + i;
 | 
			
		||||
        char* path;
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_PREFIX, test->name, "/read_header", NULL);
 | 
			
		||||
        path = g_strconcat(TEST_("read_header/"), test->name, NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_read_header);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_PREFIX, test->name, "/write_header", NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_write_header);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
        if (test->iface) {
 | 
			
		||||
            path = g_strconcat(TEST_("write_header/"), test->name, NULL);
 | 
			
		||||
            g_test_add_data_func(path, test, test_write_header);
 | 
			
		||||
            g_free(path);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								unit/unit_proxy_object/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								unit/unit_proxy_object/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_proxy_object
 | 
			
		||||
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										699
									
								
								unit/unit_proxy_object/unit_proxy_object.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										699
									
								
								unit/unit_proxy_object/unit_proxy_object.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,699 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "test_binder.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_client.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_proxy_object.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
#define DEV "/dev/xbinder"
 | 
			
		||||
#define DEV_PRIV  DEV "-private"
 | 
			
		||||
#define DEV2 "/dev/ybinder"
 | 
			
		||||
#define DEV2_PRIV  DEV2 "-private"
 | 
			
		||||
 | 
			
		||||
enum test_tx_codes {
 | 
			
		||||
    TX_CODE = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    TX_CODE2,
 | 
			
		||||
    TX_CODE3
 | 
			
		||||
};
 | 
			
		||||
#define TX_PARAM1 0x11111111
 | 
			
		||||
#define TX_PARAM2 0x22222222
 | 
			
		||||
#define TX_PARAM3 0x33333333
 | 
			
		||||
#define TX_RESULT1 0x01010101
 | 
			
		||||
#define TX_RESULT2 0x02020202
 | 
			
		||||
#define TX_PARAM_REPLY 0x11110000
 | 
			
		||||
#define TX_PARAM_DONT_REPLY 0x22220000
 | 
			
		||||
#define TX_RESULT 0x03030303
 | 
			
		||||
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-proxy-XXXXXX";
 | 
			
		||||
const char TEST_IFACE[] = "test@1.0::ITest";
 | 
			
		||||
const char TEST_IFACE2[] = "test@1.0::ITest2";
 | 
			
		||||
static const char* TEST_IFACES[] =  { TEST_IFACE, NULL };
 | 
			
		||||
static const char* TEST_IFACES2[] =  { TEST_IFACE2, NULL };
 | 
			
		||||
static const char DEFAULT_CONFIG_DATA[] =
 | 
			
		||||
    "[Protocol]\n"
 | 
			
		||||
    "Default = hidl\n"
 | 
			
		||||
    "[ServiceManager]\n"
 | 
			
		||||
    "Default = hidl\n";
 | 
			
		||||
 | 
			
		||||
typedef struct test_config {
 | 
			
		||||
    char* dir;
 | 
			
		||||
    char* file;
 | 
			
		||||
} TestConfig;
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init(
 | 
			
		||||
    TestConfig* config,
 | 
			
		||||
    char* config_data)
 | 
			
		||||
{
 | 
			
		||||
    config->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    config->file = g_build_filename(config->dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(config->file, config_data ? config_data :
 | 
			
		||||
        DEFAULT_CONFIG_DATA, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_dir = config->dir;
 | 
			
		||||
    gbinder_config_file = config->file;
 | 
			
		||||
    GDEBUG("Wrote config to %s", config->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_deinit(
 | 
			
		||||
    TestConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    remove(config->file);
 | 
			
		||||
    g_free(config->file);
 | 
			
		||||
 | 
			
		||||
    remove(config->dir);
 | 
			
		||||
    g_free(config->dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!gbinder_proxy_object_new(NULL, NULL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_basic_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    int* count = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Request handled");
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE));
 | 
			
		||||
    g_assert(code == TX_CODE);
 | 
			
		||||
 | 
			
		||||
    /* No parameters are expected */
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    (*count)++;
 | 
			
		||||
    return gbinder_local_object_new_reply(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_reply(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Reply received");
 | 
			
		||||
 | 
			
		||||
    /* No parameters are expected */
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    g_main_loop_quit((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderRemoteObject* remote_proxy;
 | 
			
		||||
    GBinderClient* proxy_client;
 | 
			
		||||
    GBinderIpc* ipc_obj;
 | 
			
		||||
    GBinderIpc* ipc_proxy;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    int fd_obj, fd_proxy, n = 0;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc_proxy = gbinder_ipc_new(DEV);
 | 
			
		||||
    ipc_obj = gbinder_ipc_new(DEV_PRIV);
 | 
			
		||||
    fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
 | 
			
		||||
    fd_obj = gbinder_driver_fd(ipc_obj->driver);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_basic_cb, &n);
 | 
			
		||||
    remote_obj = gbinder_remote_object_new(ipc_proxy,
 | 
			
		||||
        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));
 | 
			
		||||
    g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
 | 
			
		||||
    remote_proxy = gbinder_remote_object_new(ipc_obj,
 | 
			
		||||
        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_obj, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_proxy, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
 | 
			
		||||
 | 
			
		||||
    /* Perform a transaction via proxy */
 | 
			
		||||
    g_assert(gbinder_client_transact(proxy_client, TX_CODE, 0, NULL,
 | 
			
		||||
        test_basic_reply, NULL, loop));
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_assert_cmpint(n, == ,1);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd_obj);
 | 
			
		||||
    test_binder_unregister_objects(fd_proxy);
 | 
			
		||||
    gbinder_local_object_drop(obj);
 | 
			
		||||
    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_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_proxy);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_basic_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * param
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_param_cancel(
 | 
			
		||||
    gpointer req)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_remote_request_complete(req, NULL, -ECANCELED);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_param_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    int* count = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 param = 0;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE));
 | 
			
		||||
    g_assert(code == TX_CODE);
 | 
			
		||||
 | 
			
		||||
    /* Make sure that parameter got delivered intact */
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, ¶m));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    (*count)++;
 | 
			
		||||
    if (param == TX_PARAM_REPLY) {
 | 
			
		||||
        GDEBUG("Replying to request 0x%08x", param);
 | 
			
		||||
        return gbinder_local_reply_append_int32
 | 
			
		||||
            (gbinder_local_object_new_reply(obj), TX_RESULT);
 | 
			
		||||
    } else {
 | 
			
		||||
        g_assert_cmpint(param, == ,TX_PARAM_DONT_REPLY);
 | 
			
		||||
        GDEBUG("Suspending request 0x%08x", param);
 | 
			
		||||
        gbinder_remote_request_block(req);
 | 
			
		||||
        g_timeout_add_full(G_PRIORITY_DEFAULT, 50, test_param_cancel,
 | 
			
		||||
             gbinder_remote_request_ref(req), (GDestroyNotify)
 | 
			
		||||
             gbinder_remote_request_unref);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_param_reply(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * Due to limitations of our binder simulation, the result can be
 | 
			
		||||
     * delivered to a wrong thread. As a result, we only known that one
 | 
			
		||||
     * of the callbacks get NULL result and one gets NULL loop, but we
 | 
			
		||||
     * don't really know which one gets what, i.e. we have to be ready
 | 
			
		||||
     * for any combination of these parameters.
 | 
			
		||||
     *
 | 
			
		||||
     * It's too difficult to fix (without writing almost a full-blown
 | 
			
		||||
     * binder implementation), let's just live with it for now :/
 | 
			
		||||
     */
 | 
			
		||||
    if (reply) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
        gint32 result = 0;
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Reply received");
 | 
			
		||||
 | 
			
		||||
        /* Make sure that result got delivered intact */
 | 
			
		||||
        gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
        g_assert(gbinder_reader_read_int32(&reader, &result));
 | 
			
		||||
        g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
        g_assert_cmpint(result, == ,TX_RESULT);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* The cancelled one */
 | 
			
		||||
        GDEBUG("Transaction cancelled");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (loop) {
 | 
			
		||||
        g_main_loop_quit((GMainLoop*)loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_param_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderRemoteObject* remote_proxy;
 | 
			
		||||
    GBinderClient* proxy_client;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBinderIpc* ipc_obj;
 | 
			
		||||
    GBinderIpc* ipc_remote_obj;
 | 
			
		||||
    GBinderIpc* ipc_proxy;
 | 
			
		||||
    GBinderIpc* ipc_remote_proxy;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    int fd_obj, fd_proxy, n = 0;
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc_obj = gbinder_ipc_new(DEV);
 | 
			
		||||
    ipc_remote_obj = gbinder_ipc_new(DEV_PRIV);
 | 
			
		||||
    ipc_proxy = gbinder_ipc_new(DEV2);
 | 
			
		||||
    ipc_remote_proxy = gbinder_ipc_new(DEV2_PRIV);
 | 
			
		||||
    fd_proxy = gbinder_driver_fd(ipc_proxy->driver);
 | 
			
		||||
    fd_obj = gbinder_driver_fd(ipc_obj->driver);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_param_cb, &n);
 | 
			
		||||
    remote_obj = gbinder_remote_object_new(ipc_remote_obj,
 | 
			
		||||
        test_binder_register_object(fd_obj, obj, AUTO_HANDLE),
 | 
			
		||||
        REMOTE_OBJECT_CREATE_ALIVE);
 | 
			
		||||
 | 
			
		||||
    /* remote_proxy(DEV2_PRIV) => proxy (DEV2) => obj (DEV) => DEV_PRIV */
 | 
			
		||||
    g_assert(!gbinder_proxy_object_new(NULL, remote_obj));
 | 
			
		||||
    g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
 | 
			
		||||
    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_obj, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_proxy, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Perform two transactions via proxy. First one never gets completed
 | 
			
		||||
     * and eventually is cancelled, and the second one is replied to.
 | 
			
		||||
     */
 | 
			
		||||
    req = gbinder_client_new_request(proxy_client);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM_DONT_REPLY);
 | 
			
		||||
    gbinder_client_transact(proxy_client, TX_CODE, 0, req, test_param_reply,
 | 
			
		||||
        NULL, NULL);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    req = gbinder_client_new_request(proxy_client);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM_REPLY);
 | 
			
		||||
    g_assert(gbinder_client_transact(proxy_client, TX_CODE, 0, req,
 | 
			
		||||
        test_param_reply, NULL, loop));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Depending on how callbacks are scheduled, n could be 1 or 2 */
 | 
			
		||||
    g_assert_cmpint(n, > ,0);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd_obj);
 | 
			
		||||
    test_binder_unregister_objects(fd_proxy);
 | 
			
		||||
    gbinder_local_object_drop(obj);
 | 
			
		||||
    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_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_remote_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_proxy);
 | 
			
		||||
    gbinder_ipc_unref(ipc_remote_proxy);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_param(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_param_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * obj
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_obj_data {
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    GBinderLocalObject* tmp_proxy;
 | 
			
		||||
    gboolean obj_call_handled;
 | 
			
		||||
    gboolean obj_call_finished;
 | 
			
		||||
    gboolean obj2_call_handled;
 | 
			
		||||
    gboolean obj2_call_finished;
 | 
			
		||||
} TestObj;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_obj2_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestObj* test = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 param = 0;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Request 2 handled");
 | 
			
		||||
    g_assert(!test->obj2_call_handled);
 | 
			
		||||
    test->obj2_call_handled = TRUE;
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE2));
 | 
			
		||||
    g_assert(code == TX_CODE2);
 | 
			
		||||
 | 
			
		||||
    /* TX_PARAM3 parameter is expected */
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, ¶m));
 | 
			
		||||
    g_assert_cmpuint(param, == ,TX_PARAM3);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return gbinder_local_reply_append_int32
 | 
			
		||||
        (gbinder_local_object_new_reply(obj), TX_RESULT2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_obj2_reply(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 result = 0;
 | 
			
		||||
    TestObj* test = data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Reply 2 received");
 | 
			
		||||
 | 
			
		||||
    /* Make sure that result got delivered intact */
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, &result));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(result, == ,TX_RESULT2);
 | 
			
		||||
 | 
			
		||||
    g_assert(!test->obj2_call_finished);
 | 
			
		||||
    test->obj2_call_finished = TRUE;
 | 
			
		||||
    if (test->obj_call_finished) {
 | 
			
		||||
        GDEBUG("Both calls are done");
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_obj_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestObj* test = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* obj2;
 | 
			
		||||
    GBinderClient* client2;
 | 
			
		||||
    GBinderLocalRequest* req2;
 | 
			
		||||
    gint32 param = 0;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Request 1 handled");
 | 
			
		||||
    g_assert(!test->obj_call_handled);
 | 
			
		||||
    test->obj_call_handled = TRUE;
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE));
 | 
			
		||||
    g_assert(code == TX_CODE);
 | 
			
		||||
 | 
			
		||||
    /* Read parameters: TX_PARAM1, object, TX_PARAM2  */
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, ¶m));
 | 
			
		||||
    g_assert_cmpuint(param, == ,TX_PARAM1);
 | 
			
		||||
    g_assert((obj2 = gbinder_reader_read_object(&reader)));
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, ¶m));
 | 
			
		||||
    g_assert_cmpuint(param, == ,TX_PARAM2);
 | 
			
		||||
    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),
 | 
			
		||||
        obj2->handle);
 | 
			
		||||
    g_assert(test->tmp_proxy);
 | 
			
		||||
 | 
			
		||||
    /* Call remote object */
 | 
			
		||||
    client2 = gbinder_client_new(obj2, TEST_IFACE2);
 | 
			
		||||
    req2 = gbinder_client_new_request(client2);
 | 
			
		||||
    gbinder_local_request_append_int32(req2, TX_PARAM3);
 | 
			
		||||
    gbinder_client_transact(client2, TX_CODE2, 0, req2, test_obj2_reply,
 | 
			
		||||
        NULL, test);
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
    gbinder_client_unref(client2);
 | 
			
		||||
    gbinder_remote_object_unref(obj2);
 | 
			
		||||
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return gbinder_local_reply_append_int32
 | 
			
		||||
        (gbinder_local_object_new_reply(obj), TX_RESULT1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_obj_reply(
 | 
			
		||||
    GBinderClient* client,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gint32 result = 0;
 | 
			
		||||
    TestObj* test = data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Reply 1 received");
 | 
			
		||||
 | 
			
		||||
    /* Make sure that result got delivered intact */
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_read_int32(&reader, &result));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert_cmpint(result, == ,TX_RESULT1);
 | 
			
		||||
 | 
			
		||||
    g_assert(!test->obj_call_finished);
 | 
			
		||||
    test->obj_call_finished = TRUE;
 | 
			
		||||
    if (test->obj2_call_finished) {
 | 
			
		||||
        GDEBUG("Both calls are done");
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_obj_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    TestObj test;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderLocalObject* obj2;
 | 
			
		||||
    GBinderProxyObject* proxy;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderRemoteObject* remote_proxy;
 | 
			
		||||
    GBinderClient* proxy_client;
 | 
			
		||||
    GBinderIpc* ipc_obj;
 | 
			
		||||
    GBinderIpc* ipc_proxy;
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    int fd_obj, fd_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);
 | 
			
		||||
    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,
 | 
			
		||||
        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));
 | 
			
		||||
    g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj)));
 | 
			
		||||
    remote_proxy = gbinder_remote_object_new(ipc_obj,
 | 
			
		||||
        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_obj, TRUE);
 | 
			
		||||
    test_binder_set_passthrough(fd_proxy, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_obj, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd_proxy, TEST_LOOPER_ENABLE);
 | 
			
		||||
 | 
			
		||||
    /* Pass object reference via proxy */
 | 
			
		||||
    req = gbinder_client_new_request(proxy_client);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM1);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj2);
 | 
			
		||||
    gbinder_local_request_append_int32(req, TX_PARAM2);
 | 
			
		||||
    gbinder_client_transact(proxy_client, TX_CODE, 0, req, test_obj_reply,
 | 
			
		||||
        NULL, &test);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(test.obj_call_handled);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd_obj);
 | 
			
		||||
    test_binder_unregister_objects(fd_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_obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc_proxy);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_obj(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_obj_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/proxy_object/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    g_test_add_func(TEST_("param"), test_param);
 | 
			
		||||
    g_test_add_func(TEST_("obj"), test_obj);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 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
 | 
			
		||||
@@ -624,7 +624,7 @@ test_hidl_struct(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHidlStruct* test = test_data;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(test->in, test->in_size), test->in_size, NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -828,7 +828,7 @@ test_hidl_vec(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHidlVec* test = test_data;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(test->in, test->in_size), test->in_size, NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -934,7 +934,7 @@ test_hidl_string_err(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHidlStringErr* test = test_data;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(test->in, test->in_size), test->in_size, NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -968,7 +968,7 @@ test_hidl_string_err_skip(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHidlStringErr* test = test_data;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(test->in, test->in_size), test->in_size, NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1013,7 +1013,7 @@ test_fd_ok(
 | 
			
		||||
        TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1053,7 +1053,7 @@ test_fd_shortbuf(
 | 
			
		||||
        TEST_INT32_BYTES(BINDER_TYPE_FD),
 | 
			
		||||
        TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1087,7 +1087,7 @@ test_fd_badtype(
 | 
			
		||||
        TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1130,7 +1130,7 @@ test_dupfd_ok(
 | 
			
		||||
        TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1177,7 +1177,7 @@ test_dupfd_badtype(
 | 
			
		||||
        TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1220,7 +1220,7 @@ test_dupfd_badfd(
 | 
			
		||||
        TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
@@ -1259,7 +1259,7 @@ test_hidl_string(
 | 
			
		||||
    guint bufcount,
 | 
			
		||||
    const char* result)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size),
 | 
			
		||||
        size, NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
@@ -1530,7 +1530,7 @@ test_buffer(
 | 
			
		||||
        TEST_INT64_BYTES(0), TEST_INT64_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(0), TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
@@ -1578,7 +1578,7 @@ test_object(
 | 
			
		||||
        TEST_INT32_BYTES(BINDER_TYPE_HANDLE), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(1 /* handle*/), TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
@@ -1636,7 +1636,7 @@ test_object_invalid(
 | 
			
		||||
        TEST_INT32_BYTES(42 /* invalid type */), TEST_INT32_BYTES(0),
 | 
			
		||||
        TEST_INT64_BYTES(1 /* handle*/), TEST_INT64_BYTES(0)
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(input, sizeof(input)), sizeof(input), NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
@@ -1670,7 +1670,7 @@ test_vec(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    /* Using 64-bit I/O */
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    BinderObject64 obj;
 | 
			
		||||
@@ -1723,7 +1723,7 @@ test_hidl_string_vec(
 | 
			
		||||
    gsize size,
 | 
			
		||||
    const char* const* result)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size),
 | 
			
		||||
        size, NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -68,10 +68,10 @@ void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg, 1);
 | 
			
		||||
    GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg, 2);
 | 
			
		||||
    GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg,1,TRUE);
 | 
			
		||||
    GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg,2,TRUE);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj1);
 | 
			
		||||
    g_assert(obj2);
 | 
			
		||||
@@ -84,7 +84,7 @@ test_basic(
 | 
			
		||||
    g_assert(gbinder_remote_object_ref(obj1) == obj1);
 | 
			
		||||
    gbinder_remote_object_unref(obj1); /* Compensate the above reference */
 | 
			
		||||
    g_assert(!gbinder_remote_object_add_death_handler(obj1, NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_ipc_get_remote_object(ipc, 1, TRUE) == obj1);
 | 
			
		||||
    g_assert(gbinder_object_registry_get_remote(reg, 1, FALSE) == obj1);
 | 
			
		||||
    gbinder_remote_object_unref(obj1); /* Compensate the above reference */
 | 
			
		||||
    gbinder_remote_object_unref(obj1);
 | 
			
		||||
    gbinder_remote_object_unref(obj2);
 | 
			
		||||
@@ -107,20 +107,20 @@ test_dead_done(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_dead(
 | 
			
		||||
test_dead_run(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint handle = 1;
 | 
			
		||||
    const guint h = 1;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_ipc_get_remote_object
 | 
			
		||||
        (ipc, handle, FALSE);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE);
 | 
			
		||||
    gulong id = gbinder_remote_object_add_death_handler
 | 
			
		||||
        (obj, test_dead_done, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_dead_binder(fd, handle);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_br_dead_binder(fd, h);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_assert(gbinder_remote_object_is_dead(obj));
 | 
			
		||||
 | 
			
		||||
@@ -128,9 +128,18 @@ test_dead(
 | 
			
		||||
    gbinder_remote_object_remove_handler(obj, 0); /* has no effect */
 | 
			
		||||
    gbinder_remote_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_dead(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_dead_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -37,6 +37,7 @@
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_local_reply_p.h"
 | 
			
		||||
#include "gbinder_remote_reply_p.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_intarray.h>
 | 
			
		||||
@@ -46,6 +47,43 @@ static TestOpt test_opt;
 | 
			
		||||
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', 0x85)
 | 
			
		||||
#define BINDER_OBJECT_SIZE_64 (GBINDER_MAX_BINDER_OBJECT_SIZE)
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Dummy GBinderObjectRegistry functions
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
reg_dummy_ref_unref(
 | 
			
		||||
    GBinderObjectRegistry* reg)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
reg_dummy_get_local(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    void* pointer)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
reg_dummy_get_remote(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    REMOTE_REGISTRY_CREATE create)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GBinderObjectRegistryFunctions reg_dummy_fn = {
 | 
			
		||||
    .ref = reg_dummy_ref_unref,
 | 
			
		||||
    .unref = reg_dummy_ref_unref,
 | 
			
		||||
    .get_local = reg_dummy_get_local,
 | 
			
		||||
    .get_remote = reg_dummy_get_remote
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -83,7 +121,8 @@ test_empty(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply,
 | 
			
		||||
        gbinder_buffer_new(driver, NULL, 0, NULL));
 | 
			
		||||
@@ -103,7 +142,9 @@ test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
@@ -112,6 +153,7 @@ test_basic(
 | 
			
		||||
    g_assert(!gbinder_remote_reply_read_object(reply));
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -129,7 +171,8 @@ test_int32(
 | 
			
		||||
    guint32 out1 = 0;
 | 
			
		||||
    gint32 out2 = 0;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
@@ -159,7 +202,8 @@ test_int64(
 | 
			
		||||
    guint64 out1 = 0;
 | 
			
		||||
    gint64 out2 = 0;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
@@ -187,7 +231,8 @@ test_string8(
 | 
			
		||||
        'b', 'a', 'r', 0x00
 | 
			
		||||
    };
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
@@ -214,7 +259,8 @@ test_string16(
 | 
			
		||||
        TEST_INT16_BYTES('r'), 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(®);
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
@@ -238,7 +284,7 @@ void
 | 
			
		||||
test_to_local(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 reply_data [] = {
 | 
			
		||||
    static const guint8 reply_bytes [] = {
 | 
			
		||||
        /* 32-bit integer */
 | 
			
		||||
        TEST_INT32_BYTES(42),
 | 
			
		||||
        /* 64-bit NULL flat_binder_object */
 | 
			
		||||
@@ -249,32 +295,33 @@ test_to_local(
 | 
			
		||||
    };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(dev, NULL);
 | 
			
		||||
    GBinderRemoteReply* req = gbinder_remote_reply_new(NULL);
 | 
			
		||||
    GBinderLocalReply* req2;
 | 
			
		||||
    GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) };
 | 
			
		||||
    GBinderRemoteReply* rr = gbinder_remote_reply_new(®);
 | 
			
		||||
    GBinderLocalReply* lr;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    const GByteArray* bytes;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
    guint8* req_data = g_memdup(reply_data, sizeof(reply_data));
 | 
			
		||||
    guint8* reply_data = g_memdup(reply_bytes, sizeof(reply_bytes));
 | 
			
		||||
    void** objects = g_new0(void*, 2);
 | 
			
		||||
 | 
			
		||||
    /* Skip the 32-bit integer */
 | 
			
		||||
    objects[0] = req_data + 4;
 | 
			
		||||
    gbinder_remote_reply_set_data(req, gbinder_buffer_new(driver, req_data,
 | 
			
		||||
        sizeof(reply_data), objects));
 | 
			
		||||
    objects[0] = reply_data + 4;
 | 
			
		||||
    gbinder_remote_reply_set_data(rr, gbinder_buffer_new(driver, reply_data,
 | 
			
		||||
        sizeof(reply_bytes), objects));
 | 
			
		||||
 | 
			
		||||
    /* Convert to GBinderLocalReply */
 | 
			
		||||
    req2 = gbinder_remote_reply_copy_to_local(req);
 | 
			
		||||
    data = gbinder_local_reply_data(req2);
 | 
			
		||||
    lr = gbinder_remote_reply_copy_to_local(rr);
 | 
			
		||||
    data = gbinder_local_reply_data(lr);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    bytes = data->bytes;
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 4);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(bytes->len == sizeof(reply_data));
 | 
			
		||||
    g_assert(bytes->len == sizeof(reply_bytes));
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(req);
 | 
			
		||||
    gbinder_local_reply_unref(req2);
 | 
			
		||||
    gbinder_remote_reply_unref(rr);
 | 
			
		||||
    gbinder_local_reply_unref(lr);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -38,6 +38,7 @@
 | 
			
		||||
#include "gbinder_remote_request_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_object_converter.h"
 | 
			
		||||
#include "gbinder_output_data.h"
 | 
			
		||||
#include "gbinder_io.h"
 | 
			
		||||
 | 
			
		||||
@@ -54,6 +55,8 @@ static TestOpt test_opt;
 | 
			
		||||
    TEST_INT32_BYTES(3), \
 | 
			
		||||
    TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), \
 | 
			
		||||
    TEST_INT16_BYTES('o'), 0x00, 0x00
 | 
			
		||||
#define HIDL_RPC_HEADER \
 | 
			
		||||
    'f', 'o', 'o', 0x00
 | 
			
		||||
 | 
			
		||||
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', 0x85)
 | 
			
		||||
#define BINDER_OBJECT_SIZE_64 (GBINDER_MAX_BINDER_OBJECT_SIZE)
 | 
			
		||||
@@ -78,6 +81,7 @@ test_null(
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_copy_to_local(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_convert_to_local(NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_pid(NULL) == (pid_t)(-1));
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_euid(NULL) == (uid_t)(-1));
 | 
			
		||||
    g_assert(!gbinder_remote_request_read_int32(NULL, NULL));
 | 
			
		||||
@@ -87,6 +91,7 @@ test_null(
 | 
			
		||||
    g_assert(!gbinder_remote_request_read_string8(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_read_string16(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_read_object(NULL));
 | 
			
		||||
    g_assert(!gbinder_object_converter_handle_to_local(NULL, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -250,6 +255,15 @@ test_string16(
 | 
			
		||||
 * to_local
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
test_to_local_convert_none(
 | 
			
		||||
    GBinderObjectConverter* convert,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_to_local(
 | 
			
		||||
@@ -265,10 +279,26 @@ test_to_local(
 | 
			
		||||
        TEST_INT64_BYTES(0),                    /* handle */
 | 
			
		||||
        TEST_INT64_BYTES(0)                     /* cookie */
 | 
			
		||||
    };
 | 
			
		||||
    static const guint8 request_data_hidl [] = {
 | 
			
		||||
        HIDL_RPC_HEADER,
 | 
			
		||||
        /* 32-bit integer */
 | 
			
		||||
        TEST_INT32_BYTES(42),
 | 
			
		||||
        /* 64-bit NULL flat_binder_object */
 | 
			
		||||
        TEST_INT32_BYTES(BINDER_TYPE_BINDER),   /* hdr.type */
 | 
			
		||||
        TEST_INT32_BYTES(0x17f),                /* flags */
 | 
			
		||||
        TEST_INT64_BYTES(0),                    /* handle */
 | 
			
		||||
        TEST_INT64_BYTES(0)                     /* cookie */
 | 
			
		||||
    };
 | 
			
		||||
    static const GBinderObjectConverterFunctions convert_f = {
 | 
			
		||||
        .handle_to_local = test_to_local_convert_none
 | 
			
		||||
    };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* dev2 = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(dev, NULL);
 | 
			
		||||
    GBinderDriver* driver2 = gbinder_driver_new(dev2, NULL);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev), 0, 0);
 | 
			
		||||
    GBinderObjectConverter convert;
 | 
			
		||||
    GBinderLocalRequest* req2;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    const GByteArray* bytes;
 | 
			
		||||
@@ -293,10 +323,42 @@ test_to_local(
 | 
			
		||||
    g_assert(offsets->data[0] == 4);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(bytes->len == sizeof(request_data));
 | 
			
		||||
    g_assert(!memcmp(request_data, bytes->data, bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
 | 
			
		||||
    /* The same with gbinder_remote_request_translate_to_local() */
 | 
			
		||||
    req2 = gbinder_remote_request_convert_to_local(req, NULL);
 | 
			
		||||
    data = gbinder_local_request_data(req2);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    bytes = data->bytes;
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 4);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(bytes->len == sizeof(request_data));
 | 
			
		||||
    g_assert(!memcmp(request_data, bytes->data, bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
 | 
			
		||||
    /* Different driver actually requires translation */
 | 
			
		||||
    memset(&convert, 0, sizeof(convert));
 | 
			
		||||
    convert.f = &convert_f;
 | 
			
		||||
    convert.io = gbinder_driver_io(driver2);
 | 
			
		||||
    convert.protocol = gbinder_driver_protocol(driver2);
 | 
			
		||||
    req2 = gbinder_remote_request_convert_to_local(req, &convert);
 | 
			
		||||
    data = gbinder_local_request_data(req2);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    bytes = data->bytes;
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 4);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(bytes->len == sizeof(request_data_hidl));
 | 
			
		||||
    g_assert(!memcmp(request_data_hidl, bytes->data, bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
    gbinder_driver_unref(driver2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 *
 | 
			
		||||
@@ -34,10 +34,12 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
@@ -47,6 +49,7 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager-XXXXXX";
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
@@ -155,7 +158,8 @@ typedef struct test_servicemanager {
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* sm)
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
@@ -167,14 +171,16 @@ GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
    if (gutil_strv_contains(self->services, name)) {
 | 
			
		||||
        if (!self->remote) {
 | 
			
		||||
            self->remote = gbinder_ipc_get_remote_object
 | 
			
		||||
                (gbinder_client_ipc(sm->client), 1, TRUE);
 | 
			
		||||
            self->remote = gbinder_object_registry_get_remote
 | 
			
		||||
                (gbinder_ipc_object_registry(gbinder_client_ipc(sm->client)),
 | 
			
		||||
                     1, TRUE);
 | 
			
		||||
        }
 | 
			
		||||
        *status = GBINDER_STATUS_OK;
 | 
			
		||||
        return gbinder_remote_object_ref(self->remote);
 | 
			
		||||
@@ -189,7 +195,8 @@ int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
@@ -224,7 +231,7 @@ test_hwservicemanager_check_name(
 | 
			
		||||
{
 | 
			
		||||
    TestHwServiceManager* self = TEST_HWSERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
    return (!name || self->reject_name) ? 
 | 
			
		||||
    return (!name || self->reject_name) ?
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_INVALID :
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_NORMALIZE;
 | 
			
		||||
}
 | 
			
		||||
@@ -281,7 +288,6 @@ test_hwservicemanager_class_init(
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = TEST_HWSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
 | 
			
		||||
    klass->list = test_servicemanager_list;
 | 
			
		||||
    klass->get_service = test_servicemanager_get_service;
 | 
			
		||||
    klass->add_service = test_servicemanager_add_service;
 | 
			
		||||
@@ -292,12 +298,10 @@ test_hwservicemanager_class_init(
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = test_hwservicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type(TEST_TYPE_HWSERVICEMANAGER,
 | 
			
		||||
        dev);
 | 
			
		||||
    return TEST_TYPE_HWSERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -325,7 +329,7 @@ test_defservicemanager_check_name(
 | 
			
		||||
{
 | 
			
		||||
    TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
    return (!name || self->reject_name) ? 
 | 
			
		||||
    return (!name || self->reject_name) ?
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_INVALID :
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
}
 | 
			
		||||
@@ -365,7 +369,6 @@ test_defservicemanager_class_init(
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = TEST_DEFSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
    klass->list = test_servicemanager_list;
 | 
			
		||||
    klass->get_service = test_servicemanager_get_service;
 | 
			
		||||
    klass->add_service = test_servicemanager_add_service;
 | 
			
		||||
@@ -374,12 +377,17 @@ test_defservicemanager_class_init(
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = test_defservicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type(TEST_TYPE_DEFSERVICEMANAGER,
 | 
			
		||||
        dev);
 | 
			
		||||
    return TEST_TYPE_DEFSERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    return TEST_TYPE_DEFSERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -391,6 +399,7 @@ void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!gbinder_servicemanager_new(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));
 | 
			
		||||
@@ -423,7 +432,7 @@ test_invalid(
 | 
			
		||||
{
 | 
			
		||||
    int status = 0;
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    gulong id = 0;
 | 
			
		||||
 | 
			
		||||
@@ -466,9 +475,9 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
 | 
			
		||||
@@ -484,6 +493,124 @@ test_basic(
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * legacy
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_legacy(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* otherdev = "/dev/otherbinder";
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_servicemanager_exit();
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = NULL;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_hwservicemanager_new(dev);
 | 
			
		||||
    g_assert(TEST_IS_HWSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_defaultservicemanager_new(dev);
 | 
			
		||||
    g_assert(TEST_IS_DEFSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* Legacy default */
 | 
			
		||||
    ipc = gbinder_ipc_new(otherdev);
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(otherdev);
 | 
			
		||||
    g_assert(TEST_IS_DEFSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_servicemanager_exit();
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * config
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* strange_name = "/dev/notbinder";
 | 
			
		||||
    const char* legacy_name = "/dev/legacybinder";
 | 
			
		||||
    char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    char* file = g_build_filename(dir, "test.conf", NULL);
 | 
			
		||||
 | 
			
		||||
    static const char config[] =
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "Default = hidl\n"
 | 
			
		||||
        "/dev/binder = hidl\n" /* Redefined name for /dev/binder */
 | 
			
		||||
        "/dev/hwbinder = foo\n" /* Invalid name */
 | 
			
		||||
        "/dev/legacybinder = aidl\n";
 | 
			
		||||
 | 
			
		||||
    /* Reset the state */
 | 
			
		||||
    gbinder_servicemanager_exit();
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    /* Write the config file */
 | 
			
		||||
    g_assert(g_file_set_contents(file, config, -1, NULL));
 | 
			
		||||
    gbinder_config_file = file;
 | 
			
		||||
 | 
			
		||||
    /* Unknown device instantiates the default */
 | 
			
		||||
    ipc = gbinder_ipc_new(strange_name);
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(strange_name);
 | 
			
		||||
    g_assert(TEST_IS_HWSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* This one was redefined */
 | 
			
		||||
    ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(GBINDER_DEFAULT_BINDER);
 | 
			
		||||
    g_assert(TEST_IS_HWSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* This one was not (since name was invalid) */
 | 
			
		||||
    ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER);
 | 
			
		||||
    g_assert(TEST_IS_HWSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* This one points to legacy manager */
 | 
			
		||||
    ipc = gbinder_ipc_new(legacy_name);
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(legacy_name);
 | 
			
		||||
    g_assert(TEST_IS_DEFSERVICEMANAGER(sm));
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    /* Clear the state */
 | 
			
		||||
    gbinder_servicemanager_exit();
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_file = NULL;
 | 
			
		||||
 | 
			
		||||
    remove(file);
 | 
			
		||||
    remove(dir);
 | 
			
		||||
    g_free(file);
 | 
			
		||||
    g_free(dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -494,9 +621,9 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_not_present(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
 | 
			
		||||
@@ -519,9 +646,9 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_wait(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const glong forever = (test_opt.flags & TEST_FLAG_DEBUG) ?
 | 
			
		||||
        (TEST_TIMEOUT_SEC * 1000) : -1;
 | 
			
		||||
@@ -559,6 +686,7 @@ test_wait(
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -569,9 +697,9 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_wait_long(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    gulong id;
 | 
			
		||||
@@ -604,6 +732,7 @@ test_wait_long(
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -614,9 +743,9 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_wait_async(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
@@ -647,6 +776,7 @@ test_wait_async(
 | 
			
		||||
    gbinder_servicemanager_remove_all_handlers(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -656,11 +786,10 @@ test_wait_async(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_death(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
test_death_run()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
@@ -683,7 +812,7 @@ test_death(
 | 
			
		||||
 | 
			
		||||
    /* Generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* No registrations must have occured */
 | 
			
		||||
@@ -695,9 +824,18 @@ test_death(
 | 
			
		||||
    gbinder_servicemanager_remove_all_handlers(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_death()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_death_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * reanimate
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -716,7 +854,7 @@ test_reanimate_quit(
 | 
			
		||||
 | 
			
		||||
        /* Disable looper and reanimate the object */
 | 
			
		||||
        GDEBUG("Reanimating...");
 | 
			
		||||
        test_binder_set_looper_enabled(fd, FALSE);
 | 
			
		||||
        test_binder_set_looper_enabled(fd, TEST_LOOPER_DISABLE);
 | 
			
		||||
        test_binder_br_transaction_complete(fd);
 | 
			
		||||
        test_binder_br_reply(fd, 0, 0, NULL);
 | 
			
		||||
    }
 | 
			
		||||
@@ -728,7 +866,7 @@ test_reanimate(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
@@ -753,7 +891,7 @@ test_reanimate(
 | 
			
		||||
 | 
			
		||||
    /* Generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* No registrations must have occured */
 | 
			
		||||
@@ -766,6 +904,8 @@ test_reanimate(
 | 
			
		||||
    gbinder_servicemanager_remove_all_handlers(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -777,13 +917,13 @@ static
 | 
			
		||||
void
 | 
			
		||||
test_reuse(
 | 
			
		||||
    void)
 | 
			
		||||
{ 
 | 
			
		||||
{
 | 
			
		||||
    const char* binder_dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* vndbinder_dev = "/dev/vpnbinder";
 | 
			
		||||
    const char* hwbinder_dev = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    GBinderIpc* binder_ipc = gbinder_ipc_new(binder_dev, NULL);
 | 
			
		||||
    GBinderIpc* vndbinder_ipc = gbinder_ipc_new(vndbinder_dev, NULL);
 | 
			
		||||
    GBinderIpc* hwbinder_ipc = gbinder_ipc_new(hwbinder_dev, NULL);
 | 
			
		||||
    GBinderIpc* binder_ipc = gbinder_ipc_new(binder_dev);
 | 
			
		||||
    GBinderIpc* vndbinder_ipc = gbinder_ipc_new(vndbinder_dev);
 | 
			
		||||
    GBinderIpc* hwbinder_ipc = gbinder_ipc_new(hwbinder_dev);
 | 
			
		||||
    GBinderServiceManager* m1;
 | 
			
		||||
    GBinderServiceManager* m2;
 | 
			
		||||
    GBinderServiceManager* vnd1;
 | 
			
		||||
@@ -823,6 +963,7 @@ test_reuse(
 | 
			
		||||
    gbinder_ipc_unref(binder_ipc);
 | 
			
		||||
    gbinder_ipc_unref(vndbinder_ipc);
 | 
			
		||||
    gbinder_ipc_unref(hwbinder_ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -835,7 +976,7 @@ test_notify_type(
 | 
			
		||||
    GType t,
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestHwServiceManager* test;
 | 
			
		||||
    const char* name = "foo";
 | 
			
		||||
@@ -867,6 +1008,7 @@ test_notify_type(
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id2);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -902,7 +1044,7 @@ test_list(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestHwServiceManager* test;
 | 
			
		||||
@@ -924,6 +1066,7 @@ test_list(
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -950,7 +1093,7 @@ test_get(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestHwServiceManager* test;
 | 
			
		||||
@@ -987,6 +1130,7 @@ test_get(
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1011,7 +1155,7 @@ test_add(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestHwServiceManager* test;
 | 
			
		||||
@@ -1031,8 +1175,10 @@ test_add(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_assert(gutil_strv_contains(test->services, "foo"));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1048,6 +1194,8 @@ int main(int argc, char* argv[])
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("invalid"), test_invalid);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    g_test_add_func(TEST_("legacy"), test_legacy);
 | 
			
		||||
    g_test_add_func(TEST_("config"), test_config);
 | 
			
		||||
    g_test_add_func(TEST_("not_present"), test_not_present);
 | 
			
		||||
    g_test_add_func(TEST_("wait"), test_wait);
 | 
			
		||||
    g_test_add_func(TEST_("wait_long"), test_wait_long);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager_aidl/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager_aidl/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_aidl
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										613
									
								
								unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,613 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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_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 <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    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 ServiceManagerAidlClass;
 | 
			
		||||
typedef struct service_manager_aidl {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
} ServiceManagerAidl;
 | 
			
		||||
 | 
			
		||||
#define SERVICE_MANAGER_AIDL_TYPE (service_manager_aidl_get_type())
 | 
			
		||||
#define SERVICE_MANAGER_AIDL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        SERVICE_MANAGER_AIDL_TYPE, ServiceManagerAidl))
 | 
			
		||||
G_DEFINE_TYPE(ServiceManagerAidl, service_manager_aidl, \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
servicemanager_aidl_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = user_data;
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    guint32 num;
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    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) {
 | 
			
		||||
                GDEBUG("Found name '%s' => %p", str, remote_obj);
 | 
			
		||||
                gbinder_local_reply_append_remote_object(reply, 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);
 | 
			
		||||
        if (str && remote_obj && gbinder_reader_read_uint32(&reader, &num)) {
 | 
			
		||||
            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:
 | 
			
		||||
        if (gbinder_remote_request_read_uint32(req, &num)) {
 | 
			
		||||
            if (num < g_hash_table_size(self->objects)) {
 | 
			
		||||
                GList* keys = g_hash_table_get_keys(self->objects);
 | 
			
		||||
                GList* l = g_list_nth(keys, num);
 | 
			
		||||
 | 
			
		||||
                reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
                gbinder_local_reply_append_string16(reply, l->data);
 | 
			
		||||
                g_list_free(keys);
 | 
			
		||||
                *status = GBINDER_STATUS_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Index %u out of bounds", num);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
ServiceManagerAidl*
 | 
			
		||||
servicemanager_aidl_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = g_object_new(SERVICE_MANAGER_AIDL_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_aidl_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
 | 
			
		||||
void
 | 
			
		||||
servicemanager_aidl_free(
 | 
			
		||||
    ServiceManagerAidl* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
service_manager_aidl_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(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_aidl_parent_class)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
service_manager_aidl_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_aidl_parent_class)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    G_OBJECT_CLASS(service_manager_aidl_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl_init(
 | 
			
		||||
    ServiceManagerAidl* 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_aidl_class_init(
 | 
			
		||||
    ServiceManagerAidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = service_manager_aidl_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        service_manager_aidl_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        service_manager_aidl_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    if (user_data) {
 | 
			
		||||
        g_main_loop_quit(user_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_none_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_run()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
 | 
			
		||||
        loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(g_hash_table_size(smsvc->objects), == ,1);
 | 
			
		||||
    g_assert(g_hash_table_contains(smsvc->objects, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_get_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_list {
 | 
			
		||||
    char** list;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
} TestList;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_list_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestList* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Got %u name(s)", gutil_strv_length(services));
 | 
			
		||||
    g_strfreev(test->list);
 | 
			
		||||
    test->list = services;
 | 
			
		||||
    g_main_loop_quit(test->loop);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list_run()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestList test;
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
 | 
			
		||||
    /* Request the list and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(test.list);
 | 
			
		||||
    g_assert(!test.list[0]);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, test.loop));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(test.list[0], == ,name);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    g_strfreev(test.list);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_list_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(name);
 | 
			
		||||
    GDEBUG("'%s' is registered", name);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_run()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* svc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_set_max_threads(ipc, 1);
 | 
			
		||||
 | 
			
		||||
    /* Start watching */
 | 
			
		||||
    id = gbinder_servicemanager_add_registration_handler(sm, name,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    /* Register the object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, NULL));
 | 
			
		||||
 | 
			
		||||
    /* test_notify_cb will stop the loop */
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(svc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_notify_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify2
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify2_run()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, TRUE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name1 = "name1";
 | 
			
		||||
    const char* name2 = "name2";
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id1, id2;
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_set_max_threads(ipc, 1);
 | 
			
		||||
 | 
			
		||||
    /* Register the object synchronously (twice)*/
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name1, obj);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name1,obj),==,0);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(sm, name1, NULL));
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name2, obj);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name2,obj),==,0);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(sm, name2, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Watch for the first name to create internal name watcher */
 | 
			
		||||
    id1 = gbinder_servicemanager_add_registration_handler(sm, name1,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id1);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now watch for the second name */
 | 
			
		||||
    id2 = gbinder_servicemanager_add_registration_handler(sm, name2,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id2);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id1);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id2);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify2()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_notify2_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_aidl/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    g_test_add_func(TEST_("notify"), test_notify);
 | 
			
		||||
    g_test_add_func(TEST_("notify2"), test_notify2);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager_aidl2/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager_aidl2/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_aidl2
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										451
									
								
								unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,451 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] =
 | 
			
		||||
    "gbinder-test-servicemanager_aidl2-XXXXXX";
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    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 ServiceManagerAidl2Class;
 | 
			
		||||
typedef struct service_manager_aidl2 {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
} ServiceManagerAidl2;
 | 
			
		||||
 | 
			
		||||
#define SERVICE_MANAGER_AIDL2_TYPE (service_manager_aidl2_get_type())
 | 
			
		||||
#define SERVICE_MANAGER_AIDL2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        SERVICE_MANAGER_AIDL2_TYPE, ServiceManagerAidl2))
 | 
			
		||||
G_DEFINE_TYPE(ServiceManagerAidl2, service_manager_aidl2, \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
servicemanager_aidl2_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = user_data;
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    guint32 num, allow_isolated, dumpsys_priority;
 | 
			
		||||
    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) {
 | 
			
		||||
                GDEBUG("Found name '%s' => %p", str, remote_obj);
 | 
			
		||||
                gbinder_local_reply_append_remote_object(reply, 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);
 | 
			
		||||
        if (str && remote_obj &&
 | 
			
		||||
            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, &num) &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
 | 
			
		||||
            if (num < g_hash_table_size(self->objects)) {
 | 
			
		||||
                GList* keys = g_hash_table_get_keys(self->objects);
 | 
			
		||||
                GList* l = g_list_nth(keys, num);
 | 
			
		||||
 | 
			
		||||
                /* Ignore dumpsys_priority */
 | 
			
		||||
                reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
                gbinder_local_reply_append_string16(reply, l->data);
 | 
			
		||||
                g_list_free(keys);
 | 
			
		||||
                *status = GBINDER_STATUS_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Index %u out of bounds", num);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
ServiceManagerAidl2*
 | 
			
		||||
servicemanager_aidl2_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = g_object_new(SERVICE_MANAGER_AIDL2_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_aidl2_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_aidl2_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(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_aidl2_parent_class)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
service_manager_aidl2_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_aidl2_parent_class)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl2_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    G_OBJECT_CLASS(service_manager_aidl2_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl2_init(
 | 
			
		||||
    ServiceManagerAidl2* 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_aidl2_class_init(
 | 
			
		||||
    ServiceManagerAidl2Class* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = service_manager_aidl2_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        service_manager_aidl2_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        service_manager_aidl2_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;
 | 
			
		||||
    ServiceManagerAidl2* 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 = aidl2\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "Default = aidl2\n"
 | 
			
		||||
        "/dev/binder = aidl2\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_aidl2_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_aidl2/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    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:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										6
									
								
								unit/unit_servicemanager_hidl/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								unit/unit_servicemanager_hidl/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_hidl
 | 
			
		||||
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										481
									
								
								unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										481
									
								
								unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,481 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 "test_binder.h"
 | 
			
		||||
#include "test_servicemanager_hidl.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
#define MAIN_DEV GBINDER_DEFAULT_HWBINDER
 | 
			
		||||
#define OTHER_DEV GBINDER_DEFAULT_HWBINDER "-private"
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-svcmgr-hidl-XXXXXX";
 | 
			
		||||
static const char DEFAULT_CONFIG_DATA[] =
 | 
			
		||||
    "[Protocol]\n"
 | 
			
		||||
    MAIN_DEV " = hidl\n"
 | 
			
		||||
    OTHER_DEV " = hidl\n"
 | 
			
		||||
    "[ServiceManager]\n"
 | 
			
		||||
    MAIN_DEV " = hidl\n";
 | 
			
		||||
 | 
			
		||||
typedef struct test_config {
 | 
			
		||||
    char* dir;
 | 
			
		||||
    char* file;
 | 
			
		||||
} TestConfig;
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init(
 | 
			
		||||
    TestConfig* config,
 | 
			
		||||
    char* config_data)
 | 
			
		||||
{
 | 
			
		||||
    config->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    config->file = g_build_filename(config->dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(config->file, config_data ? config_data :
 | 
			
		||||
        DEFAULT_CONFIG_DATA, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_dir = config->dir;
 | 
			
		||||
    gbinder_config_file = config->file;
 | 
			
		||||
    GDEBUG("Wrote config to %s", config->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_deinit(
 | 
			
		||||
    TestConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    remove(config->file);
 | 
			
		||||
    g_free(config->file);
 | 
			
		||||
 | 
			
		||||
    remove(config->dir);
 | 
			
		||||
    g_free(config->dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_impl_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    TestServiceManagerHidl* sm = test_servicemanager_hidl_new(ipc);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm),
 | 
			
		||||
        GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return sm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("Name added");
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    if (user_data) {
 | 
			
		||||
        g_main_loop_quit(user_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_none_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_run()
 | 
			
		||||
{
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    smsvc = test_servicemanager_impl_new(OTHER_DEV);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* This one fails because of unexpected name format */
 | 
			
		||||
    g_assert(!gbinder_servicemanager_get_service_sync(sm, "test", NULL));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
 | 
			
		||||
        loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(test_servicemanager_hidl_object_count(smsvc), == ,1);
 | 
			
		||||
    g_assert(test_servicemanager_hidl_lookup(smsvc, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_get_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_list {
 | 
			
		||||
    char** list;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
} TestList;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_list_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestList* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Got %u name(s)", gutil_strv_length(services));
 | 
			
		||||
    g_strfreev(test->list);
 | 
			
		||||
    test->list = services;
 | 
			
		||||
    g_main_loop_quit(test->loop);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list_run()
 | 
			
		||||
{
 | 
			
		||||
    TestList test;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    smsvc = test_servicemanager_impl_new(OTHER_DEV);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* Request the list and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(test.list);
 | 
			
		||||
    g_assert(!test.list[0]);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, test.loop));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(test.list[0], == ,name);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
 | 
			
		||||
    g_strfreev(test.list);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_list_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_notify {
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    int notify_count;
 | 
			
		||||
    gboolean name_added;
 | 
			
		||||
} TestNotify;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_never(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestNotify* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Name added");
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_assert(!test->name_added);
 | 
			
		||||
    test->name_added = TRUE;
 | 
			
		||||
    if (test->notify_count) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestNotify* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_assert(name);
 | 
			
		||||
    GDEBUG("'%s' is registered", name);
 | 
			
		||||
    g_assert_cmpint(test->notify_count, == ,0);
 | 
			
		||||
    test->notify_count++;
 | 
			
		||||
    /* Exit the loop after both things happen */
 | 
			
		||||
    if (test->name_added) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_run()
 | 
			
		||||
{
 | 
			
		||||
    TestNotify test;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    test.smsvc = test_servicemanager_impl_new(OTHER_DEV);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TEST_LOOPER_ENABLE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* This one fails because of invalid names */
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL,
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, "",
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, ",",
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Start watching */
 | 
			
		||||
    id = gbinder_servicemanager_add_registration_handler(sm, name,
 | 
			
		||||
        test_notify_cb, &test);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    /* Register the object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_notify_add_cb, &test));
 | 
			
		||||
 | 
			
		||||
    /* The loop quits after the name is added and notification is received */
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(test.smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, test.loop);
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify()
 | 
			
		||||
{
 | 
			
		||||
    test_run_in_context(&test_opt, test_notify_run);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_hidl/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    g_test_add_func(TEST_("notify"), test_notify);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2019-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
@@ -55,15 +55,6 @@ test_quit(
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_quit_when_destroyed(
 | 
			
		||||
    gpointer loop,
 | 
			
		||||
    GObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    test_quit_later((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_setup_ping(
 | 
			
		||||
@@ -87,6 +78,7 @@ typedef struct test_servicemanager {
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    char** services;
 | 
			
		||||
    gboolean block_add;
 | 
			
		||||
    int add_fail;
 | 
			
		||||
    int add_result;
 | 
			
		||||
} TestServiceManager;
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +93,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    char** ret;
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
@@ -118,7 +111,8 @@ GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    *status = (-ENOENT);
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -129,19 +123,34 @@ int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
    int result;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    if (!gutil_strv_contains(self->services, name)) {
 | 
			
		||||
        self->services = gutil_strv_add(self->services, name);
 | 
			
		||||
    }
 | 
			
		||||
    while (self->block_add) {
 | 
			
		||||
        g_cond_wait(&self->cond, &self->mutex);
 | 
			
		||||
    if (self->add_fail > 0) {
 | 
			
		||||
        self->add_fail--;
 | 
			
		||||
        result = -EFAULT;
 | 
			
		||||
    } else {
 | 
			
		||||
        result = self->add_result;
 | 
			
		||||
        if (result == GBINDER_STATUS_OK) {
 | 
			
		||||
            if (!gutil_strv_contains(self->services, name)) {
 | 
			
		||||
                self->services = gutil_strv_add(self->services, name);
 | 
			
		||||
            }
 | 
			
		||||
            if (self->block_add) {
 | 
			
		||||
                while (self->block_add) {
 | 
			
		||||
                    g_cond_wait(&self->cond, &self->mutex);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                test_binder_br_dead_binder(gbinder_driver_fd(obj->ipc->driver),
 | 
			
		||||
                    GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return self->add_result;
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -202,7 +211,6 @@ test_servicemanager_class_init(
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = TEST_SERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
    klass->list = test_servicemanager_list;
 | 
			
		||||
    klass->get_service = test_servicemanager_get_service;
 | 
			
		||||
    klass->add_service = test_servicemanager_add_service;
 | 
			
		||||
@@ -212,18 +220,24 @@ test_servicemanager_class_init(
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
/* Avoid pulling in the actual objects */
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new(dev);
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    return TEST_TYPE_SERVICEMANAGER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -236,7 +250,7 @@ test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
@@ -263,7 +277,7 @@ test_basic(
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
@@ -276,7 +290,7 @@ test_basic(
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
    g_assert_cmpstr(sn->name, == ,obj_name);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_servicename_ref(sn) == sn);
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
@@ -284,13 +298,8 @@ test_basic(
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    /* We need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -306,7 +315,7 @@ test_present(
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
@@ -321,10 +330,10 @@ test_present(
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
    g_assert_cmpstr(sn->name, == ,obj_name);
 | 
			
		||||
 | 
			
		||||
    /* Immediately generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    test_binder_br_dead_binder(fd, GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
@@ -333,13 +342,9 @@ test_present(
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    /* We need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -371,7 +376,7 @@ test_not_present(
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
@@ -388,7 +393,7 @@ test_not_present(
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
    g_assert_cmpstr(sn->name, == ,obj_name);
 | 
			
		||||
 | 
			
		||||
    /* Make the next presence detection PING succeed */
 | 
			
		||||
    test_binder_br_transaction_complete_later(fd);
 | 
			
		||||
@@ -399,13 +404,51 @@ test_not_present(
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    /* We need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * retry
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_retry(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderServiceName* sn;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    TEST_SERVICEMANAGER(sm)->add_fail = 1;
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert_cmpstr(sn->name, == ,obj_name);
 | 
			
		||||
 | 
			
		||||
    /* Need looper for death notifications */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -421,7 +464,7 @@ test_cancel(
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
@@ -443,10 +486,10 @@ test_cancel(
 | 
			
		||||
    /* This adds the name but the call blocks */
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
    g_assert_cmpstr(sn->name, == ,obj_name);
 | 
			
		||||
 | 
			
		||||
    /* Immediately generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    test_binder_br_dead_binder(fd, GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
@@ -463,12 +506,9 @@ test_cancel(
 | 
			
		||||
    g_cond_signal(&test->cond);
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
 | 
			
		||||
    /* We need to wait until GBinderIpc is destroyed */
 | 
			
		||||
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait(&test_opt, loop);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -486,6 +526,7 @@ int main(int argc, char* argv[])
 | 
			
		||||
    g_test_add_func(TEST_("present_ok"), test_present_ok);
 | 
			
		||||
    g_test_add_func(TEST_("present_err"), test_present_err);
 | 
			
		||||
    g_test_add_func(TEST_("not_present"), test_not_present);
 | 
			
		||||
    g_test_add_func(TEST_("retry"), test_retry);
 | 
			
		||||
    g_test_add_func(TEST_("cancel"), test_cancel);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user