Compare commits
	
		
			116 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2d878c2391 | ||
| 
						 | 
					6dec867cd3 | ||
| 
						 | 
					d8bd7b9366 | ||
| 
						 | 
					b52e4c6dee | ||
| 
						 | 
					21046af42f | ||
| 
						 | 
					3cbe1d6ac8 | ||
| 
						 | 
					b4b1a99a27 | ||
| 
						 | 
					f5a2d481e3 | ||
| 
						 | 
					da57a15852 | ||
| 
						 | 
					9d0ac624c5 | ||
| 
						 | 
					b8ccc80b87 | ||
| 
						 | 
					27e2dac22e | ||
| 
						 | 
					a4ec4e382d | ||
| 
						 | 
					30df5faf64 | ||
| 
						 | 
					0aa29d8a91 | ||
| 
						 | 
					3ace986e4a | ||
| 
						 | 
					064ab48eee | ||
| 
						 | 
					12c1725ab1 | ||
| 
						 | 
					ba2ddf4163 | ||
| 
						 | 
					3d16384acd | ||
| 
						 | 
					0f73626025 | ||
| 
						 | 
					84fb44e519 | ||
| 
						 | 
					aafe23396a | ||
| 
						 | 
					51f270df67 | ||
| 
						 | 
					e9c404de92 | ||
| 
						 | 
					141bda151b | ||
| 
						 | 
					e952380ce6 | ||
| 
						 | 
					721f5dc469 | ||
| 
						 | 
					434b17eefe | ||
| 
						 | 
					af6a1c84ef | ||
| 
						 | 
					a0ce447a1f | ||
| 
						 | 
					f24145f407 | ||
| 
						 | 
					11c173b2e4 | ||
| 
						 | 
					aff816d10c | ||
| 
						 | 
					e1acaa3bdb | ||
| 
						 | 
					c63743ac51 | ||
| 
						 | 
					6832d9bf46 | ||
| 
						 | 
					4c3ccbc06f | ||
| 
						 | 
					f82596c372 | ||
| 
						 | 
					4921a6ab8d | ||
| 
						 | 
					ef9c242a59 | ||
| 
						 | 
					a83c9937a5 | ||
| 
						 | 
					cfa3ad4d9e | ||
| 
						 | 
					ffc9638ebb | ||
| 
						 | 
					000534654d | ||
| 
						 | 
					68e9358d02 | ||
| 
						 | 
					37e3859f8f | ||
| 
						 | 
					e79b940b0d | ||
| 
						 | 
					d8dfe3f289 | ||
| 
						 | 
					394c286ee5 | ||
| 
						 | 
					307bd6942c | ||
| 
						 | 
					148b53e862 | ||
| 
						 | 
					5c8cb0a013 | ||
| 
						 | 
					4d644e0584 | ||
| 
						 | 
					dce9c8b3d1 | ||
| 
						 | 
					b117ee6404 | ||
| 
						 | 
					c3f783bf7e | ||
| 
						 | 
					4c65a6eded | ||
| 
						 | 
					c382cab922 | ||
| 
						 | 
					199fd4ed61 | ||
| 
						 | 
					f86d62fbf8 | ||
| 
						 | 
					3ea82dc384 | ||
| 
						 | 
					03ae5834ee | ||
| 
						 | 
					e39b5c20ee | ||
| 
						 | 
					d6e131eb6e | ||
| 
						 | 
					1c36b5f142 | ||
| 
						 | 
					827bd0b59f | ||
| 
						 | 
					2dab057652 | ||
| 
						 | 
					488fbc5b63 | ||
| 
						 | 
					17f511d7a3 | ||
| 
						 | 
					63e633c0ec | ||
| 
						 | 
					f46448c236 | ||
| 
						 | 
					43023be32d | ||
| 
						 | 
					4811d51c5d | ||
| 
						 | 
					821dabca3d | ||
| 
						 | 
					587f4ebb50 | ||
| 
						 | 
					0c0b25fcd1 | ||
| 
						 | 
					bdf07d04d4 | ||
| 
						 | 
					6936675eb9 | ||
| 
						 | 
					c39bf4b802 | ||
| 
						 | 
					5a43b1b091 | ||
| 
						 | 
					3e039e033c | ||
| 
						 | 
					6a2af83ea3 | ||
| 
						 | 
					5a12df240b | ||
| 
						 | 
					2ea7d91fc7 | ||
| 
						 | 
					d9903e7398 | ||
| 
						 | 
					39b69f27ee | ||
| 
						 | 
					31a1d19b4e | ||
| 
						 | 
					f4a923c3dc | ||
| 
						 | 
					171ff7d1e4 | ||
| 
						 | 
					5cfbf22b81 | ||
| 
						 | 
					5a35ed5ea1 | ||
| 
						 | 
					1978359f15 | ||
| 
						 | 
					fef6543f1a | ||
| 
						 | 
					b1a9200803 | ||
| 
						 | 
					fada30868a | ||
| 
						 | 
					8414e9485f | ||
| 
						 | 
					f6d8d485bc | ||
| 
						 | 
					6ea0d6c631 | ||
| 
						 | 
					31c6c05c1e | ||
| 
						 | 
					d855d695db | ||
| 
						 | 
					922cc82029 | ||
| 
						 | 
					80498378f2 | ||
| 
						 | 
					51856865df | ||
| 
						 | 
					d26dcca37e | ||
| 
						 | 
					5ac02fcb2e | ||
| 
						 | 
					110cd65779 | ||
| 
						 | 
					67e665b619 | ||
| 
						 | 
					d1c431c370 | ||
| 
						 | 
					2167a82c73 | ||
| 
						 | 
					d113d3bf4a | ||
| 
						 | 
					b2206adae5 | ||
| 
						 | 
					8075cce1b1 | ||
| 
						 | 
					29e4c79f75 | ||
| 
						 | 
					3b299d3345 | ||
| 
						 | 
					7421fff380 | 
							
								
								
									
										3
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -1,2 +1,5 @@
 | 
			
		||||
Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Matti Lehtimäki <matti.lehtimaki@gmail.com>
 | 
			
		||||
Franz-Josef Haider <franz.haider@jolla.com>
 | 
			
		||||
Juho Hämäläinen <juho.hamalainen@jolla.com>
 | 
			
		||||
Andrew Branson <andrew.branson@jolla.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
@@ -12,9 +12,9 @@ are met:
 | 
			
		||||
  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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								Makefile
									
									
									
									
									
								
							@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
.PHONY: clean all debug release test
 | 
			
		||||
.PHONY: print_debug_so print_release_so
 | 
			
		||||
.PHONY: print_debug_lib print_release_lib
 | 
			
		||||
.PHONY: print_debug_lib print_release_lib print_coverage_lib
 | 
			
		||||
.PHONY: print_debug_link print_release_link
 | 
			
		||||
.PHONY: print_debug_path print_release_path
 | 
			
		||||
 | 
			
		||||
@@ -24,7 +24,7 @@ all: debug release pkgconfig
 | 
			
		||||
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 0
 | 
			
		||||
VERSION_RELEASE = 10
 | 
			
		||||
VERSION_RELEASE = 35
 | 
			
		||||
 | 
			
		||||
# Version for pkg-config
 | 
			
		||||
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
 | 
			
		||||
@@ -62,6 +62,8 @@ SRC = \
 | 
			
		||||
  gbinder_remote_reply.c \
 | 
			
		||||
  gbinder_remote_request.c \
 | 
			
		||||
  gbinder_rpc_protocol.c \
 | 
			
		||||
  gbinder_servicename.c \
 | 
			
		||||
  gbinder_servicepoll.c \
 | 
			
		||||
  gbinder_writer.c
 | 
			
		||||
 | 
			
		||||
SRC += \
 | 
			
		||||
@@ -247,7 +249,7 @@ $(COVERAGE_LIB): $(COVERAGE_OBJS)
 | 
			
		||||
	$(AR) rc $@ $?
 | 
			
		||||
	ranlib $@
 | 
			
		||||
 | 
			
		||||
$(PKGCONFIG): $(LIB_NAME).pc.in
 | 
			
		||||
$(PKGCONFIG): $(LIB_NAME).pc.in Makefile
 | 
			
		||||
	sed -e 's/\[version\]/'$(PCVERSION)/g $< > $@
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								README
									
									
									
									
									
								
							@@ -1 +1,7 @@
 | 
			
		||||
GLib-style interface to binder (Android IPC mechanism)
 | 
			
		||||
 | 
			
		||||
Provides:
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										164
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										164
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,167 @@
 | 
			
		||||
libgbinder (1.0.35) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added binder-ping example
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 25 Feb 2020 13:58:19 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.34) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Better cleanup on unload to prevent crashes on exit
 | 
			
		||||
  * Fixed rare memory leak in GBinderServiceManager
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 16 Dec 2019 12:25:56 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.33) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Reuse loopers
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 13 Sep 2019 15:57:47 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.32) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Refuse to perform transactions with dead objects
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 17 May 2019 15:57:30 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.31) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Invalidate handle when remote object dies
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 13 May 2019 18:05:35 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.30) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added gbinder_local_object_new()
 | 
			
		||||
  * Added gbinder_remote_object_ipc()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 20 Feb 2019 11:59:08 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.29) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added gbinder_servicemanager_new_local_object2()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 14 Feb 2019 18:17:53 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.28) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Set type for local nulls to BINDER_TYPE_WEAK_BINDER
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 29 Jan 2019 02:49:10 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.27) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed outgoing oneway transactions
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 24 Jan 2019 18:55:16 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.26) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Implement PING and INTERFACE transactions
 | 
			
		||||
  * Add GBinderServiceName API
 | 
			
		||||
  * Added gbinder_reader_read_string16_utf16()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 23 Jan 2019 17:43:41 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.25) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added ServiceManager presence API
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_wait()
 | 
			
		||||
    gbinder_servicemanager_is_present()
 | 
			
		||||
    gbinder_servicemanager_add_presence_handler()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 22 Jan 2019 16:03:57 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.24) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Revert "Make sure NULL objects are passed as BINDER_TYPE_WEAK_HANDLE"
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 18 Jan 2019 21:36:32 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.23) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added gbinder_reader_read_hidl_string_c()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 15 Jan 2019 15:16:41 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.22) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added gbinder_client_interface()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 10 Jan 2019 14:09:44 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.21) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added API to overwrite prefix length
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 18 Dec 2018 14:05:14 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.20) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added API to block incoming requests
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 17 Dec 2018 16:06:43 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.19) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added GBinderWriter memory allocation and cleanup API
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 14 Dec 2018 16:27:51 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.18) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Implemented support for file descritors
 | 
			
		||||
  * Allow GBinderClient without RPC header
 | 
			
		||||
  * Added binder-dump test
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Mon, 10 Dec 2018 13:17:22 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.17) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added gbinder_writer_append_string16_utf16()
 | 
			
		||||
  * Added gbinder_reader_read_nullable_string16_utf16()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Fri, 07 Dec 2018 02:54:07 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.16) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Added GBinderHidlVec and GBinderHidlString types
 | 
			
		||||
  * Added gbinder_reader_copy()
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 06 Dec 2018 19:03:32 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.15) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Implemented service polling for old servicemanager
 | 
			
		||||
  * Added new tests and improved coverage for existing ones
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 05 Dec 2018 12:11:34 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.14) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Changed bool padding from 0xff to 0x00
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 27 Nov 2018 17:20:18 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.13) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Support for service registration notifications
 | 
			
		||||
  * Make sure looper is started before gbinder_ipc_looper_new() returns
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Sat, 17 Nov 2018 01:52:28 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.12) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Add byte array reader and writer
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 31 Oct 2018 17:04:38 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.11) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Use BINDER_TYPE_WEAK_HANDLE for NULL objects
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 24 Oct 2018 18:57:28 +0300
 | 
			
		||||
 | 
			
		||||
libgbinder (1.0.10) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed dependencies for unit tests
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,13 +2,13 @@ Source: libgbinder
 | 
			
		||||
Section: libs
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Build-Depends: debhelper (>= 7), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.29)
 | 
			
		||||
Build-Depends: debhelper (>= 7), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.35)
 | 
			
		||||
Standards-Version: 3.8.4
 | 
			
		||||
 | 
			
		||||
Package: libgbinder
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: libglibutil (>= 1.0.29), ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Depends: libglibutil (>= 1.0.35), ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Description: Binder client library
 | 
			
		||||
 | 
			
		||||
Package: libgbinder-dev
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
@@ -12,9 +12,9 @@ are met:
 | 
			
		||||
  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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -44,6 +44,7 @@
 | 
			
		||||
#include "gbinder_remote_object.h"
 | 
			
		||||
#include "gbinder_remote_reply.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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,10 @@ void
 | 
			
		||||
gbinder_client_unref(
 | 
			
		||||
    GBinderClient* client);
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_client_interface(
 | 
			
		||||
    GBinderClient* client); /* since 1.0.22 */
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_client_new_request(
 | 
			
		||||
    GBinderClient* client);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -37,6 +37,14 @@
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data) /* Since 1.0.30 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_ref(
 | 
			
		||||
    GBinderLocalObject* obj);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -53,7 +53,7 @@ struct gbinder_reader {
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_at_end(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
    const GBinderReader* reader);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_byte(
 | 
			
		||||
@@ -95,6 +95,15 @@ gbinder_reader_read_double(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gdouble* value);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_reader_read_fd(
 | 
			
		||||
    GBinderReader* reader); /* Since 1.0.18 */
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_reader_read_dup_fd(
 | 
			
		||||
    GBinderReader* reader) /* Since 1.0.18 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_nullable_object(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
@@ -116,7 +125,7 @@ gbinder_reader_read_buffer(
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_read_hidl_struct1(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize size); /* since 1.0.9 */
 | 
			
		||||
    gsize size); /* Since 1.0.9 */
 | 
			
		||||
 | 
			
		||||
#define gbinder_reader_read_hidl_struct(reader,type) \
 | 
			
		||||
    ((const type*)gbinder_reader_read_hidl_struct1(reader, sizeof(type)))
 | 
			
		||||
@@ -131,7 +140,7 @@ const void*
 | 
			
		||||
gbinder_reader_read_hidl_vec1(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* count,
 | 
			
		||||
    guint expected_elemsize); /* since 1.0.9 */
 | 
			
		||||
    guint expected_elemsize); /* Since 1.0.9 */
 | 
			
		||||
 | 
			
		||||
#define gbinder_reader_read_hidl_type_vec(reader,type,count) \
 | 
			
		||||
    ((const type*)gbinder_reader_read_hidl_vec1(reader, count, sizeof(type)))
 | 
			
		||||
@@ -143,6 +152,13 @@ gbinder_reader_read_hidl_string(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_reader_read_hidl_string_c(
 | 
			
		||||
    GBinderReader* reader); /* Since 1.0.23 */
 | 
			
		||||
 | 
			
		||||
#define gbinder_reader_skip_hidl_string(reader) \
 | 
			
		||||
    (gbinder_reader_read_hidl_string_c(reader) != NULL)
 | 
			
		||||
 | 
			
		||||
char**
 | 
			
		||||
gbinder_reader_read_hidl_string_vec(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
@@ -165,17 +181,38 @@ gbinder_reader_read_nullable_string16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    char** out);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_nullable_string16_utf16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    const gunichar2** out,
 | 
			
		||||
    gsize* len); /* Since 1.0.17 */
 | 
			
		||||
 | 
			
		||||
const gunichar2*
 | 
			
		||||
gbinder_reader_read_string16_utf16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* len); /* Since 1.0.26 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_skip_string16(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_read_byte_array(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* len); /* Since 1.0.12 */
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_read(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
    const GBinderReader* reader);
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_remaining(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
    const GBinderReader* reader);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_reader_copy(
 | 
			
		||||
    GBinderReader* dest,
 | 
			
		||||
    const GBinderReader* src); /* Since 1.0.16 */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -51,6 +51,10 @@ void
 | 
			
		||||
gbinder_remote_object_unref(
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_remote_object_ipc(
 | 
			
		||||
    GBinderRemoteObject* obj); /* Since 1.0.30 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_object_is_dead(
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,16 @@ gbinder_remote_request_copy_to_local(
 | 
			
		||||
    GBinderRemoteRequest* req) /* since 1.0.6 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_block(
 | 
			
		||||
    GBinderRemoteRequest* req); /* Since 1.0.20 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_complete(
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    GBinderLocalReply* reply,
 | 
			
		||||
    int status); /* Since 1.0.20 */
 | 
			
		||||
 | 
			
		||||
/* Convenience function to decode requests with just one data item */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -37,6 +37,12 @@
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*GBinderServiceManagerFunc)(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
/* GBinderServiceManagerListFunc callback returns TRUE to keep the services
 | 
			
		||||
 * list, otherwise the caller will deallocate it. */
 | 
			
		||||
typedef
 | 
			
		||||
@@ -61,6 +67,13 @@ void
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*GBinderServiceManagerRegistrationFunc)(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_new(
 | 
			
		||||
    const char* dev);
 | 
			
		||||
@@ -80,6 +93,13 @@ gbinder_servicemanager_new_local_object(
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_servicemanager_new_local_object2(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data); /* Since 1.0.29 */
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_ref(
 | 
			
		||||
    GBinderServiceManager* sm);
 | 
			
		||||
@@ -88,6 +108,15 @@ void
 | 
			
		||||
gbinder_servicemanager_unref(
 | 
			
		||||
    GBinderServiceManager* sm);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_is_present(
 | 
			
		||||
    GBinderServiceManager* sm); /* Since 1.0.25 */
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_wait(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    long max_wait_ms); /* Since 1.0.25 */
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
@@ -130,6 +159,33 @@ gbinder_servicemanager_cancel(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    gulong id);
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_add_presence_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderServiceManagerFunc func,
 | 
			
		||||
    void* user_data); /* Since 1.0.25 */
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_add_registration_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderServiceManagerRegistrationFunc func,
 | 
			
		||||
    void* user_data); /* Since 1.0.13 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_remove_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    gulong id); /* Since 1.0.13 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_remove_handlers(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    gulong* ids,
 | 
			
		||||
    guint count); /* Since 1.0.25 */
 | 
			
		||||
 | 
			
		||||
#define gbinder_servicemanager_remove_all_handlers(r,ids) \
 | 
			
		||||
    gbinder_servicemanager_remove_handlers(sm, ids, G_N_ELEMENTS(ids))
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_H */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								include/gbinder_servicename.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								include/gbinder_servicename.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019 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_SERVICENAME_H
 | 
			
		||||
#define GBINDER_SERVICENAME_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types.h"
 | 
			
		||||
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
/* Since 1.0.26 */
 | 
			
		||||
 | 
			
		||||
struct gbinder_servicename {
 | 
			
		||||
    const char* name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBinderServiceName*
 | 
			
		||||
gbinder_servicename_new(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
GBinderServiceName*
 | 
			
		||||
gbinder_servicename_ref(
 | 
			
		||||
    GBinderServiceName* name);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_unref(
 | 
			
		||||
    GBinderServiceName* name);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICENAME_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -61,6 +61,7 @@ G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_buffer GBinderBuffer;
 | 
			
		||||
typedef struct gbinder_client GBinderClient;
 | 
			
		||||
typedef struct gbinder_ipc GBinderIpc;
 | 
			
		||||
typedef struct gbinder_local_object GBinderLocalObject;
 | 
			
		||||
typedef struct gbinder_local_reply GBinderLocalReply;
 | 
			
		||||
typedef struct gbinder_local_request GBinderLocalRequest;
 | 
			
		||||
@@ -68,10 +69,35 @@ typedef struct gbinder_reader GBinderReader;
 | 
			
		||||
typedef struct gbinder_remote_object GBinderRemoteObject;
 | 
			
		||||
typedef struct gbinder_remote_reply GBinderRemoteReply;
 | 
			
		||||
typedef struct gbinder_remote_request GBinderRemoteRequest;
 | 
			
		||||
typedef struct gbinder_servicename GBinderServiceName;
 | 
			
		||||
typedef struct gbinder_servicemanager GBinderServiceManager;
 | 
			
		||||
typedef struct gbinder_writer GBinderWriter;
 | 
			
		||||
typedef struct gbinder_parent GBinderParent;
 | 
			
		||||
 | 
			
		||||
/* Basic HIDL types */
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_vec {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const void* ptr;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 count;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
} GBinderHidlVec;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_VEC_BUFFER_OFFSET (0)
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_string {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const char* str;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 len;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
} GBinderHidlString;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_STRING_BUFFER_OFFSET (0)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Each RPC call is identified by the interface name returned
 | 
			
		||||
 * by gbinder_remote_request_interface() the transaction code.
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,12 @@ gbinder_writer_append_string16_len(
 | 
			
		||||
    const char* utf8,
 | 
			
		||||
    gssize num_bytes);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string16_utf16(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length); /* Since 1.0.17 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string8(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
@@ -108,6 +114,21 @@ gbinder_writer_append_bytes(
 | 
			
		||||
    const void* data,
 | 
			
		||||
    gsize size);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_fd(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    int fd); /* Since 1.0.18 */
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_writer_bytes_written(
 | 
			
		||||
    GBinderWriter* writer); /* since 1.0.21 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_overwrite_int32(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    gint32 value); /* since 1.0.21 */
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
gbinder_writer_append_buffer_object_with_parent(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
@@ -149,6 +170,40 @@ gbinder_writer_append_remote_object(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_byte_array(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const void* byte_array,
 | 
			
		||||
    gint32 len); /* since 1.0.12 */
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_malloc(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize size); /* since 1.0.19 */
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_malloc0(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    gsize size); /* since 1.0.19 */
 | 
			
		||||
 | 
			
		||||
#define gbinder_writer_new(writer,type) \
 | 
			
		||||
    ((type*) gbinder_writer_malloc(writer, sizeof(type)))
 | 
			
		||||
 | 
			
		||||
#define gbinder_writer_new0(writer,type) \
 | 
			
		||||
    ((type*) gbinder_writer_malloc0(writer, sizeof(type)))
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_memdup(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const void* buf,
 | 
			
		||||
    gsize size); /* since 1.0.19 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_add_cleanup(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    GDestroyNotify destroy,
 | 
			
		||||
    gpointer data); /* since 1.0.19 */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_WRITER_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
Name: libgbinder
 | 
			
		||||
Version: 1.0.10
 | 
			
		||||
Version: 1.0.35
 | 
			
		||||
Release: 0
 | 
			
		||||
Summary: Binder client library
 | 
			
		||||
Group: Development/Libraries
 | 
			
		||||
License: BSD
 | 
			
		||||
URL: https://github.com/mer-hybris/libgbinder
 | 
			
		||||
Source: %{name}-%{version}.tar.bz2
 | 
			
		||||
Requires: libglibutil >= 1.0.29
 | 
			
		||||
Requires: libglibutil >= 1.0.35
 | 
			
		||||
BuildRequires: pkgconfig(glib-2.0)
 | 
			
		||||
BuildRequires: pkgconfig(libglibutil) >= 1.0.29
 | 
			
		||||
BuildRequires: pkgconfig(libglibutil) >= 1.0.35
 | 
			
		||||
Requires(post): /sbin/ldconfig
 | 
			
		||||
Requires(postun): /sbin/ldconfig
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,54 +36,62 @@
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
struct gbinder_buffer_memory {
 | 
			
		||||
struct gbinder_buffer_contents {
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    void* buffer;
 | 
			
		||||
    gsize size;
 | 
			
		||||
    void** objects;
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_buffer_priv {
 | 
			
		||||
    GBinderBuffer pub;
 | 
			
		||||
    GBinderBufferMemory* memory;
 | 
			
		||||
    GBinderBufferContents* contents;
 | 
			
		||||
} GBinderBufferPriv;
 | 
			
		||||
 | 
			
		||||
static inline GBinderBufferPriv* gbinder_buffer_cast(GBinderBuffer* buf)
 | 
			
		||||
    { return G_CAST(buf, GBinderBufferPriv, pub); }
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderBufferMemory
 | 
			
		||||
 * GBinderBufferContents
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderBufferMemory*
 | 
			
		||||
gbinder_buffer_memory_new(
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_buffer_contents_new(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    void* buffer,
 | 
			
		||||
    gsize size)
 | 
			
		||||
    gsize size,
 | 
			
		||||
    void** objects)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBufferMemory* self = g_slice_new0(GBinderBufferMemory);
 | 
			
		||||
    GBinderBufferContents* self = g_slice_new0(GBinderBufferContents);
 | 
			
		||||
 | 
			
		||||
    g_atomic_int_set(&self->refcount, 1);
 | 
			
		||||
    self->buffer = buffer;
 | 
			
		||||
    self->size = size;
 | 
			
		||||
    self->objects = objects;
 | 
			
		||||
    self->driver = gbinder_driver_ref(driver);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_buffer_memory_free(
 | 
			
		||||
    GBinderBufferMemory* self)
 | 
			
		||||
gbinder_buffer_contents_free(
 | 
			
		||||
    GBinderBufferContents* self)
 | 
			
		||||
{
 | 
			
		||||
    if (self->objects) {
 | 
			
		||||
        gbinder_driver_close_fds(self->driver, self->objects,
 | 
			
		||||
            ((guint8*)self->buffer) + self->size);
 | 
			
		||||
        g_free(self->objects);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_driver_free_buffer(self->driver, self->buffer);
 | 
			
		||||
    gbinder_driver_unref(self->driver);
 | 
			
		||||
    g_slice_free(GBinderBufferMemory, self);
 | 
			
		||||
    g_slice_free(GBinderBufferContents, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBufferMemory*
 | 
			
		||||
gbinder_buffer_memory_ref(
 | 
			
		||||
    GBinderBufferMemory* self)
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_buffer_contents_ref(
 | 
			
		||||
    GBinderBufferContents* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
@@ -93,13 +101,13 @@ gbinder_buffer_memory_ref(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_buffer_memory_unref(
 | 
			
		||||
    GBinderBufferMemory* self)
 | 
			
		||||
gbinder_buffer_contents_unref(
 | 
			
		||||
    GBinderBufferContents* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
        if (g_atomic_int_dec_and_test(&self->refcount)) {
 | 
			
		||||
            gbinder_buffer_memory_free(self);
 | 
			
		||||
            gbinder_buffer_contents_free(self);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -111,14 +119,14 @@ gbinder_buffer_memory_unref(
 | 
			
		||||
static
 | 
			
		||||
GBinderBuffer*
 | 
			
		||||
gbinder_buffer_alloc(
 | 
			
		||||
    GBinderBufferMemory* memory,
 | 
			
		||||
    GBinderBufferContents* contents,
 | 
			
		||||
    void* data,
 | 
			
		||||
    gsize size)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBufferPriv* priv = g_slice_new0(GBinderBufferPriv);
 | 
			
		||||
    GBinderBuffer* self = &priv->pub;
 | 
			
		||||
 | 
			
		||||
    priv->memory = memory;
 | 
			
		||||
    priv->contents = contents;
 | 
			
		||||
    self->data = data;
 | 
			
		||||
    self->size = size;
 | 
			
		||||
    return self;
 | 
			
		||||
@@ -131,7 +139,7 @@ gbinder_buffer_free(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderBufferPriv* priv = gbinder_buffer_cast(self);
 | 
			
		||||
 | 
			
		||||
        gbinder_buffer_memory_unref(priv->memory);
 | 
			
		||||
        gbinder_buffer_contents_unref(priv->contents);
 | 
			
		||||
        g_slice_free(GBinderBufferPriv, priv);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -140,10 +148,12 @@ GBinderBuffer*
 | 
			
		||||
gbinder_buffer_new(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    void* data,
 | 
			
		||||
    gsize size)
 | 
			
		||||
    gsize size,
 | 
			
		||||
    void** objects)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_buffer_alloc((driver && data) ?
 | 
			
		||||
        gbinder_buffer_memory_new(driver, data, size) : NULL, data, size);
 | 
			
		||||
        gbinder_buffer_contents_new(driver, data, size, objects) : NULL,
 | 
			
		||||
        data, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBuffer*
 | 
			
		||||
@@ -153,7 +163,7 @@ gbinder_buffer_new_with_parent(
 | 
			
		||||
    gsize size)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_buffer_alloc(parent ?
 | 
			
		||||
        gbinder_buffer_memory_ref(gbinder_buffer_memory(parent)) : NULL,
 | 
			
		||||
        gbinder_buffer_contents_ref(gbinder_buffer_contents(parent)) : NULL,
 | 
			
		||||
        data, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -162,13 +172,13 @@ gbinder_buffer_data(
 | 
			
		||||
    GBinderBuffer* self,
 | 
			
		||||
    gsize* size)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBufferMemory* memory = gbinder_buffer_memory(self);
 | 
			
		||||
    GBinderBufferContents* contents = gbinder_buffer_contents(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(memory)) {
 | 
			
		||||
    if (G_LIKELY(contents)) {
 | 
			
		||||
        if (size) {
 | 
			
		||||
            *size = memory->size;
 | 
			
		||||
            *size = contents->size;
 | 
			
		||||
        }
 | 
			
		||||
        return memory->buffer;
 | 
			
		||||
        return contents->buffer;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (size) {
 | 
			
		||||
            *size = 0;
 | 
			
		||||
@@ -184,8 +194,8 @@ gbinder_buffer_driver(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderBufferPriv* priv = gbinder_buffer_cast(self);
 | 
			
		||||
 | 
			
		||||
        if (priv->memory) {
 | 
			
		||||
            return priv->memory->driver;
 | 
			
		||||
        if (priv->contents) {
 | 
			
		||||
            return priv->contents->driver;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -200,11 +210,25 @@ gbinder_buffer_io(
 | 
			
		||||
    return driver ? gbinder_driver_io(driver) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBufferMemory*
 | 
			
		||||
gbinder_buffer_memory(
 | 
			
		||||
void**
 | 
			
		||||
gbinder_buffer_objects(
 | 
			
		||||
    GBinderBuffer* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? gbinder_buffer_cast(self)->memory : NULL;
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderBufferPriv* priv = gbinder_buffer_cast(self);
 | 
			
		||||
 | 
			
		||||
        if (priv->contents) {
 | 
			
		||||
            return priv->contents->objects;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_buffer_contents(
 | 
			
		||||
    GBinderBuffer* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? gbinder_buffer_cast(self)->contents : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,8 @@ GBinderBuffer*
 | 
			
		||||
gbinder_buffer_new(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    void* data,
 | 
			
		||||
    gsize size);
 | 
			
		||||
    gsize size,
 | 
			
		||||
    void** objects);
 | 
			
		||||
 | 
			
		||||
GBinderBuffer*
 | 
			
		||||
gbinder_buffer_new_with_parent(
 | 
			
		||||
@@ -53,8 +54,8 @@ GBinderDriver*
 | 
			
		||||
gbinder_buffer_driver(
 | 
			
		||||
    GBinderBuffer* buf);
 | 
			
		||||
 | 
			
		||||
GBinderBufferMemory*
 | 
			
		||||
gbinder_buffer_memory(
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_buffer_contents(
 | 
			
		||||
    GBinderBuffer* buf);
 | 
			
		||||
 | 
			
		||||
gconstpointer
 | 
			
		||||
@@ -66,13 +67,17 @@ const GBinderIo*
 | 
			
		||||
gbinder_buffer_io(
 | 
			
		||||
    GBinderBuffer* buf);
 | 
			
		||||
 | 
			
		||||
GBinderBufferMemory*
 | 
			
		||||
gbinder_buffer_memory_ref(
 | 
			
		||||
    GBinderBufferMemory* mem);
 | 
			
		||||
void**
 | 
			
		||||
gbinder_buffer_objects(
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
GBinderBufferContents*
 | 
			
		||||
gbinder_buffer_contents_ref(
 | 
			
		||||
    GBinderBufferContents* contents);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_buffer_memory_unref(
 | 
			
		||||
    GBinderBufferMemory* mem);
 | 
			
		||||
gbinder_buffer_contents_unref(
 | 
			
		||||
    GBinderBufferContents* contents);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_BUFFER_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -48,11 +48,33 @@ struct gbinder_cleanup {
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderCleanup) == sizeof(GArray));
 | 
			
		||||
#define ELEMENT_SIZE (sizeof(GBinderCleanupItem))
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_cleanup_destroy_func(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderCleanupItem* item = data;
 | 
			
		||||
 | 
			
		||||
    item->destroy(item->pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderCleanup*
 | 
			
		||||
gbinder_cleanup_new()
 | 
			
		||||
{
 | 
			
		||||
    return (GBinderCleanup*)g_array_sized_new(FALSE, FALSE, ELEMENT_SIZE, 0);
 | 
			
		||||
    GArray* array = g_array_sized_new(FALSE, FALSE, ELEMENT_SIZE, 0);
 | 
			
		||||
 | 
			
		||||
    g_array_set_clear_func(array, gbinder_cleanup_destroy_func);
 | 
			
		||||
    return (GBinderCleanup*)array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_cleanup_reset(
 | 
			
		||||
    GBinderCleanup* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_array_set_size((GArray*)self, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -60,11 +82,6 @@ gbinder_cleanup_free(
 | 
			
		||||
    GBinderCleanup* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        guint i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < self->count; i++) {
 | 
			
		||||
            self->items[i].destroy(self->items[i].pointer);
 | 
			
		||||
        }
 | 
			
		||||
        g_array_free((GArray*)self, TRUE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -39,6 +39,10 @@ void
 | 
			
		||||
gbinder_cleanup_free(
 | 
			
		||||
    GBinderCleanup* cleanup);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_cleanup_reset(
 | 
			
		||||
    GBinderCleanup* cleanup);
 | 
			
		||||
 | 
			
		||||
GBinderCleanup*
 | 
			
		||||
gbinder_cleanup_add(
 | 
			
		||||
    GBinderCleanup* cleanup,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -75,7 +75,9 @@ gbinder_client_free(
 | 
			
		||||
    gbinder_remote_object_unref(self->remote);
 | 
			
		||||
    gbinder_local_request_unref(priv->basic_req);
 | 
			
		||||
    g_free(priv->iface);
 | 
			
		||||
    g_bytes_unref(priv->rpc_header);
 | 
			
		||||
    if (priv->rpc_header) {
 | 
			
		||||
        g_bytes_unref(priv->rpc_header);
 | 
			
		||||
    }
 | 
			
		||||
    g_slice_free(GBinderClientPriv, priv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -117,13 +119,13 @@ gbinder_client_new(
 | 
			
		||||
    GBinderRemoteObject* remote,
 | 
			
		||||
    const char* iface)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(remote) && G_LIKELY(iface)) {
 | 
			
		||||
    if (G_LIKELY(remote)) {
 | 
			
		||||
        GBinderClientPriv* priv = g_slice_new0(GBinderClientPriv);
 | 
			
		||||
        GBinderClient* self = &priv->pub;
 | 
			
		||||
        GBinderIpc* ipc = remote->ipc;
 | 
			
		||||
        GBinderOutputData* hdr;
 | 
			
		||||
        GBinderDriver* driver = remote->ipc->driver;
 | 
			
		||||
 | 
			
		||||
        g_atomic_int_set(&priv->refcount, 1);
 | 
			
		||||
        self->remote = gbinder_remote_object_ref(remote);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Generate basic request (without additional parameters) and pull
 | 
			
		||||
@@ -131,12 +133,17 @@ gbinder_client_new(
 | 
			
		||||
         * transactions which has no additional parameters. The header data
 | 
			
		||||
         * are needed for building non-trivial requests.
 | 
			
		||||
         */
 | 
			
		||||
        priv->basic_req = gbinder_driver_local_request_new(ipc->driver, iface);
 | 
			
		||||
        hdr = gbinder_local_request_data(priv->basic_req);
 | 
			
		||||
        priv->rpc_header = g_bytes_new(hdr->bytes->data, hdr->bytes->len);
 | 
			
		||||
        if (iface) {
 | 
			
		||||
            GBinderOutputData* hdr;
 | 
			
		||||
 | 
			
		||||
        self->remote = gbinder_remote_object_ref(remote);
 | 
			
		||||
        self->iface = priv->iface = g_strdup(iface);
 | 
			
		||||
            priv->basic_req = gbinder_driver_local_request_new(driver, iface);
 | 
			
		||||
            hdr = gbinder_local_request_data(priv->basic_req);
 | 
			
		||||
            priv->rpc_header = g_bytes_new(hdr->bytes->data, hdr->bytes->len);
 | 
			
		||||
            self->iface = priv->iface = g_strdup(iface);
 | 
			
		||||
        } else {
 | 
			
		||||
            priv->basic_req = gbinder_local_request_new
 | 
			
		||||
                (gbinder_driver_io(driver), NULL);
 | 
			
		||||
        }
 | 
			
		||||
        return self;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -169,6 +176,13 @@ gbinder_client_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_client_interface(
 | 
			
		||||
    GBinderClient* self) /* since 1.0.22 */
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? gbinder_client_cast(self)->iface : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_client_new_request(
 | 
			
		||||
    GBinderClient* self)
 | 
			
		||||
@@ -191,12 +205,15 @@ gbinder_client_transact_sync_reply(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (!req) {
 | 
			
		||||
            /* Default empty request (just the header, no parameters) */
 | 
			
		||||
            req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
 | 
			
		||||
                code, req, status);
 | 
			
		||||
        }
 | 
			
		||||
        return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
 | 
			
		||||
            code, req, status);
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -210,15 +227,18 @@ gbinder_client_transact_sync_oneway(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (!req) {
 | 
			
		||||
            /* Default empty request (just the header, no parameters) */
 | 
			
		||||
            req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
 | 
			
		||||
                code, req);
 | 
			
		||||
        }
 | 
			
		||||
        return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
 | 
			
		||||
            code, req);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
        return (-ESTALE);
 | 
			
		||||
    }
 | 
			
		||||
    return (-EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
@@ -233,23 +253,27 @@ gbinder_client_transact(
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
        GBinderClientTx* tx = g_slice_new0(GBinderClientTx);
 | 
			
		||||
 | 
			
		||||
        tx->client = gbinder_client_ref(self);
 | 
			
		||||
        tx->reply = reply;
 | 
			
		||||
        tx->destroy = destroy;
 | 
			
		||||
        tx->user_data = user_data;
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            GBinderClientTx* tx = g_slice_new0(GBinderClientTx);
 | 
			
		||||
 | 
			
		||||
        if (!req) {
 | 
			
		||||
            /* Default empty request (just the header, no parameters) */
 | 
			
		||||
            req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
            tx->client = gbinder_client_ref(self);
 | 
			
		||||
            tx->reply = reply;
 | 
			
		||||
            tx->destroy = destroy;
 | 
			
		||||
            tx->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                req = gbinder_client_cast(self)->basic_req;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return gbinder_ipc_transact(obj->ipc, obj->handle, code,
 | 
			
		||||
                flags, req, gbinder_client_transact_reply,
 | 
			
		||||
                gbinder_client_transact_destroy, tx);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return gbinder_ipc_transact(obj->ipc, obj->handle, code, flags, req,
 | 
			
		||||
            gbinder_client_transact_reply, gbinder_client_transact_destroy, tx);
 | 
			
		||||
    } else {
 | 
			
		||||
        return 0;
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
@@ -43,13 +44,31 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManager GBinderDefaultServiceManager;
 | 
			
		||||
typedef struct gbinder_defaultservicemanager_watch {
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    char* name;
 | 
			
		||||
    gulong handler_id;
 | 
			
		||||
    guint notify_id;
 | 
			
		||||
} GBinderDefaultServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderDefaultServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_defaultservicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderDefaultServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderDefaultServiceManager,
 | 
			
		||||
    gbinder_defaultservicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_defaultservicemanager_parent_class
 | 
			
		||||
#define GBINDER_TYPE_DEFAULTSERVICEMANAGER \
 | 
			
		||||
    gbinder_defaultservicemanager_get_type()
 | 
			
		||||
#define GBINDER_DEFAULTSERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_DEFAULTSERVICEMANAGER, \
 | 
			
		||||
    GBinderDefaultServiceManager)
 | 
			
		||||
 | 
			
		||||
enum gbinder_defaultservicemanager_calls {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
@@ -57,8 +76,6 @@ enum gbinder_defaultservicemanager_calls {
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* As a special case, ServiceManager's handle is zero */
 | 
			
		||||
#define DEFAULTSERVICEMANAGER_HANDLE (0)
 | 
			
		||||
#define DEFAULTSERVICEMANAGER_IFACE  "android.os.IServiceManager"
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
@@ -66,7 +83,76 @@ gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type
 | 
			
		||||
        (gbinder_defaultservicemanager_get_type(), dev);
 | 
			
		||||
        (GBINDER_TYPE_DEFAULTSERVICEMANAGER, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_watch_proc(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(name_added, watch->name)) {
 | 
			
		||||
        GBinderServiceManager* manager =
 | 
			
		||||
            gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
 | 
			
		||||
        if (watch->notify_id) {
 | 
			
		||||
            g_source_remove(watch->notify_id);
 | 
			
		||||
            watch->notify_id = 0;
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_service_registered(manager, name_added);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_defaultservicemanager_watch_notify(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
    GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll);
 | 
			
		||||
    char* name = g_strdup(watch->name);
 | 
			
		||||
 | 
			
		||||
    GASSERT(watch->notify_id);
 | 
			
		||||
    watch->notify_id = 0;
 | 
			
		||||
    gbinder_servicemanager_service_registered(manager, name);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_watch_free(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch = user_data;
 | 
			
		||||
 | 
			
		||||
    if (watch->notify_id) {
 | 
			
		||||
        g_source_remove(watch->notify_id);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_servicepoll_remove_handler(watch->poll, watch->handler_id);
 | 
			
		||||
    gbinder_servicepoll_unref(watch->poll);
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    g_slice_free(GBinderDefaultServiceManagerWatch, watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderDefaultServiceManagerWatch*
 | 
			
		||||
gbinder_defaultservicemanager_watch_new(
 | 
			
		||||
    GBinderDefaultServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDefaultServiceManagerWatch* watch =
 | 
			
		||||
        g_slice_new0(GBinderDefaultServiceManagerWatch);
 | 
			
		||||
 | 
			
		||||
    watch->name = g_strdup(name);
 | 
			
		||||
    watch->poll = gbinder_servicepoll_new(&manager->manager, &manager->poll);
 | 
			
		||||
    watch->handler_id = gbinder_servicepoll_add_handler(watch->poll,
 | 
			
		||||
        gbinder_defaultservicemanager_watch_proc, watch);
 | 
			
		||||
    return watch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -151,11 +237,61 @@ gbinder_defaultservicemanager_add_service(
 | 
			
		||||
    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_id =
 | 
			
		||||
            g_idle_add(gbinder_defaultservicemanager_watch_notify, watch);
 | 
			
		||||
    }
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_defaultservicemanager_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_remove(GBINDER_DEFAULTSERVICEMANAGER(manager)->watch_table,
 | 
			
		||||
        name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
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
 | 
			
		||||
@@ -163,7 +299,6 @@ void
 | 
			
		||||
gbinder_defaultservicemanager_class_init(
 | 
			
		||||
    GBinderDefaultServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->handle = DEFAULTSERVICEMANAGER_HANDLE;
 | 
			
		||||
    klass->iface = DEFAULTSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
@@ -171,6 +306,11 @@ gbinder_defaultservicemanager_class_init(
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -288,19 +288,15 @@ gbinder_driver_death_notification(
 | 
			
		||||
    guint32 cmd,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GBinderIoBuf write;
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_DEATH_NOTIFICATION_SIZE];
 | 
			
		||||
        guint32* data = (guint32*)buf;
 | 
			
		||||
    GBinderIoBuf write;
 | 
			
		||||
    guint8 buf[4 + GBINDER_MAX_DEATH_NOTIFICATION_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);
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_write(self, &write) >= 0;
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
    data[0] = cmd;
 | 
			
		||||
    memset(&write, 0, sizeof(write));
 | 
			
		||||
    write.ptr = (uintptr_t)buf;
 | 
			
		||||
    write.size = 4 + self->io->encode_death_notification(data + 1, obj);
 | 
			
		||||
    return gbinder_driver_write(self, &write) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -439,11 +435,10 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
    /* Transfer data ownership to the request */
 | 
			
		||||
    if (tx.data && tx.size) {
 | 
			
		||||
        gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size);
 | 
			
		||||
        gbinder_remote_request_set_data(req,
 | 
			
		||||
            gbinder_buffer_new(self, tx.data, tx.size),
 | 
			
		||||
            tx.objects);
 | 
			
		||||
        gbinder_remote_request_set_data(req, tx.code,
 | 
			
		||||
            gbinder_buffer_new(self, tx.data, tx.size, tx.objects));
 | 
			
		||||
    } else {
 | 
			
		||||
        g_free(tx.objects);
 | 
			
		||||
        GASSERT(!tx.objects);
 | 
			
		||||
        gbinder_driver_free_buffer(self, tx.data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -459,7 +454,7 @@ gbinder_driver_handle_transaction(
 | 
			
		||||
            &status);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GWARN("Unhandled transaction 0x%08x", tx.code);
 | 
			
		||||
        GWARN("Unhandled transaction %s 0x%08x", iface, tx.code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -536,6 +531,7 @@ gbinder_driver_handle_command(
 | 
			
		||||
    } else if (cmd == io->br.transaction) {
 | 
			
		||||
        gbinder_driver_handle_transaction(self, reg, handler, data);
 | 
			
		||||
    } else if (cmd == io->br.dead_binder) {
 | 
			
		||||
        guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE];
 | 
			
		||||
        guint64 handle = 0;
 | 
			
		||||
        GBinderRemoteObject* obj;
 | 
			
		||||
 | 
			
		||||
@@ -546,6 +542,8 @@ gbinder_driver_handle_command(
 | 
			
		||||
            gbinder_remote_object_handle_death_notification(obj);
 | 
			
		||||
            gbinder_remote_object_unref(obj);
 | 
			
		||||
        }
 | 
			
		||||
        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 {
 | 
			
		||||
@@ -637,10 +635,9 @@ gbinder_driver_txstatus(
 | 
			
		||||
            if (tx.data && tx.size) {
 | 
			
		||||
                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_buffer_new(self, tx.data, tx.size, tx.objects));
 | 
			
		||||
            } else {
 | 
			
		||||
                g_free(tx.objects);
 | 
			
		||||
                GASSERT(!tx.objects);
 | 
			
		||||
                gbinder_driver_free_buffer(self, tx.data);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -814,8 +811,13 @@ gbinder_driver_request_death_notification(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_driver_death_notification
 | 
			
		||||
        (self, self->io->bc.request_death_notification, obj);
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GVERBOSE("< BC_REQUEST_DEATH_NOTIFICATION 0x%08x", obj->handle);
 | 
			
		||||
        return gbinder_driver_death_notification(self,
 | 
			
		||||
            self->io->bc.request_death_notification, obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
@@ -823,8 +825,13 @@ gbinder_driver_clear_death_notification(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_driver_death_notification
 | 
			
		||||
        (self, self->io->bc.clear_death_notification, obj);
 | 
			
		||||
    if (G_LIKELY(obj)) {
 | 
			
		||||
        GVERBOSE("< BC_CLEAR_DEATH_NOTIFICATION 0x%08x", obj->handle);
 | 
			
		||||
        return gbinder_driver_death_notification(self,
 | 
			
		||||
            self->io->bc.clear_death_notification, obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
@@ -863,6 +870,32 @@ gbinder_driver_release(
 | 
			
		||||
    return gbinder_driver_cmd_int32(self, self->io->bc.release, handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_close_fds(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    void** objects,
 | 
			
		||||
    const void* end)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = self->io;
 | 
			
		||||
    void** ptr;
 | 
			
		||||
 | 
			
		||||
    /* Caller checks objects for NULL */
 | 
			
		||||
    for (ptr = objects; *ptr; ptr++) {
 | 
			
		||||
        void* obj = *ptr;
 | 
			
		||||
 | 
			
		||||
        GASSERT(obj < end);
 | 
			
		||||
        if (obj < end) {
 | 
			
		||||
            int fd;
 | 
			
		||||
 | 
			
		||||
            if (io->decode_fd_object(obj, (guint8*)end - (guint8*)obj, &fd)) {
 | 
			
		||||
                if (close(fd) < 0) {
 | 
			
		||||
                    GWARN("Error closing fd %d: %s", fd, strerror(errno));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_free_buffer(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
@@ -1014,6 +1047,28 @@ gbinder_driver_transact(
 | 
			
		||||
    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,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -97,6 +97,12 @@ gbinder_driver_release(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    guint32 handle);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_close_fds(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
    void** objects,
 | 
			
		||||
    const void* end);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_driver_free_buffer(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
@@ -125,6 +131,12 @@ gbinder_driver_transact(
 | 
			
		||||
    GBinderLocalRequest* request,
 | 
			
		||||
    GBinderRemoteReply* reply);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_driver_ping(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
    guint32 handle);
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_driver_local_request_new(
 | 
			
		||||
    GBinderDriver* self,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,20 +35,36 @@
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_object.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
#include <gbinder_remote_request.h>
 | 
			
		||||
#include <gbinder_reader.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManager GBinderHwServiceManager;
 | 
			
		||||
typedef struct gbinder_hwservicemanager_watch {
 | 
			
		||||
    char* name;
 | 
			
		||||
    GBinderLocalObject* callback;
 | 
			
		||||
} GBinderHwServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderHwServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_hwservicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderHwServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderHwServiceManager,
 | 
			
		||||
    gbinder_hwservicemanager,
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
enum gbinder_hwservicemanager_calls {
 | 
			
		||||
    GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    ADD_TRANSACTION,
 | 
			
		||||
@@ -60,9 +76,74 @@ enum gbinder_hwservicemanager_calls {
 | 
			
		||||
    REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* As a special case, ServiceManager's handle is zero */
 | 
			
		||||
#define HWSERVICEMANAGER_HANDLE (0)
 | 
			
		||||
enum gbinder_hwservicemanager_notifications {
 | 
			
		||||
    ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define HWSERVICEMANAGER_IFACE  "android.hidl.manager@1.0::IServiceManager"
 | 
			
		||||
#define HWSERVICEMANAGER_NOTIFICATION_IFACE \
 | 
			
		||||
    "android.hidl.manager@1.0::IServiceNotification"
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_handle_registration(
 | 
			
		||||
    GBinderHwServiceManager* self,
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    char* fqname = gbinder_reader_read_hidl_string(reader);
 | 
			
		||||
    char* name = gbinder_reader_read_hidl_string(reader);
 | 
			
		||||
    gboolean preexisting;
 | 
			
		||||
 | 
			
		||||
    /* (string fqName, string name, bool preexisting) */
 | 
			
		||||
    if (fqname && name && gbinder_reader_read_bool(reader, &preexisting) &&
 | 
			
		||||
        gbinder_reader_at_end(reader)) {
 | 
			
		||||
        char* full_name = g_strconcat(fqname, "/", name, NULL);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("%s %s", full_name, preexisting ? "true" : "false");
 | 
			
		||||
        gbinder_servicemanager_service_registered(&self->manager, full_name);
 | 
			
		||||
        g_free(full_name);
 | 
			
		||||
    } else {
 | 
			
		||||
        GWARN("Failed to parse IServiceNotification::onRegistration payload");
 | 
			
		||||
    }
 | 
			
		||||
    g_free(fqname);
 | 
			
		||||
    g_free(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_hwservicemanager_notification(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(user_data);
 | 
			
		||||
    const char* iface = gbinder_remote_request_interface(req);
 | 
			
		||||
 | 
			
		||||
    if (!g_strcmp0(iface, HWSERVICEMANAGER_NOTIFICATION_IFACE)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        switch (code) {
 | 
			
		||||
        case ON_REGISTRATION_TRANSACTION:
 | 
			
		||||
            GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u onRegistration",
 | 
			
		||||
                code);
 | 
			
		||||
            gbinder_hwservicemanager_handle_registration(self, &reader);
 | 
			
		||||
            *status = GBINDER_STATUS_OK;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u", code);
 | 
			
		||||
            *status = GBINDER_STATUS_FAILED;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("%s %u", iface, code);
 | 
			
		||||
        *status = GBINDER_STATUS_FAILED;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
@@ -75,7 +156,7 @@ gbinder_hwservicemanager_new(
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_hwservicemanager_list(
 | 
			
		||||
    GBinderHwServiceManager* self)
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
 | 
			
		||||
@@ -111,6 +192,7 @@ gbinder_hwservicemanager_get_service(
 | 
			
		||||
    /* e.g. "android.hardware.radio@1.1::IRadio/slot1" */
 | 
			
		||||
    const char* sep = strchr(fqinstance, '/');
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
 | 
			
		||||
    if (sep) {
 | 
			
		||||
        GBinderRemoteReply* reply;
 | 
			
		||||
        GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
@@ -170,11 +252,123 @@ gbinder_hwservicemanager_add_service(
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_watch_free(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManagerWatch* watch = data;
 | 
			
		||||
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    gbinder_local_object_drop(watch->callback);
 | 
			
		||||
    g_free(watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
gbinder_hwservicemanager_check_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    if (name) {
 | 
			
		||||
        const gsize len = strlen(name);
 | 
			
		||||
        static const char allowed_chars[] = "./0123456789:@"
 | 
			
		||||
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | 
			
		||||
            "abcdefghijklmnopqrstuvwxyz";
 | 
			
		||||
 | 
			
		||||
        if (len && strspn(name, allowed_chars) == len) {
 | 
			
		||||
            return strchr(name, '/') ?
 | 
			
		||||
                GBINDER_SERVICEMANAGER_NAME_NORMALIZE :
 | 
			
		||||
                GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return GBINDER_SERVICEMANAGER_NAME_INVALID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char*
 | 
			
		||||
gbinder_hwservicemanager_normalize_name(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    /* Slash must be there, see gbinder_hwservicemanager_check_name() above */
 | 
			
		||||
    return g_strndup(name, strchr(name, '/') - name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_hwservicemanager_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(manager);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(manager->client);
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderHwServiceManagerWatch* watch =
 | 
			
		||||
        g_new0(GBinderHwServiceManagerWatch, 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);
 | 
			
		||||
    g_hash_table_replace(self->watch_table, watch->name, watch);
 | 
			
		||||
 | 
			
		||||
    /* registerForNotifications(string fqName, string name,
 | 
			
		||||
     * IServiceNotification callback) generates (bool success); */
 | 
			
		||||
    gbinder_local_request_append_hidl_string(req, name);
 | 
			
		||||
    gbinder_local_request_append_hidl_string(req, "");
 | 
			
		||||
    gbinder_local_request_append_local_object(req, watch->callback);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(manager->client,
 | 
			
		||||
        REGISTER_FOR_NOTIFICATIONS_TRANSACTION, req, &status);
 | 
			
		||||
 | 
			
		||||
    if (status == GBINDER_STATUS_OK && reply) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_init_reader(reply, &reader);
 | 
			
		||||
        if (gbinder_reader_read_int32(&reader, &status) &&
 | 
			
		||||
            status == GBINDER_STATUS_OK) {
 | 
			
		||||
            gbinder_reader_read_bool(&reader, &success);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    if (!success) {
 | 
			
		||||
        /* unwatch() won't be called if we return FALSE */
 | 
			
		||||
        g_hash_table_remove(self->watch_table, watch->name);
 | 
			
		||||
    }
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_remove(GBINDER_HWSERVICEMANAGER(manager)->watch_table, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_init(
 | 
			
		||||
    GBinderHwServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        NULL, gbinder_hwservicemanager_watch_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_hwservicemanager_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->watch_table);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -182,7 +376,6 @@ void
 | 
			
		||||
gbinder_hwservicemanager_class_init(
 | 
			
		||||
    GBinderHwServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->handle = HWSERVICEMANAGER_HANDLE;
 | 
			
		||||
    klass->iface = HWSERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
 | 
			
		||||
@@ -190,6 +383,11 @@ gbinder_hwservicemanager_class_init(
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -128,9 +128,13 @@ GBINDER_IO_FN(encode_local_object)(
 | 
			
		||||
    struct flat_binder_object* dest = out;
 | 
			
		||||
 | 
			
		||||
    memset(dest, 0, sizeof(*dest));
 | 
			
		||||
    dest->hdr.type = BINDER_TYPE_BINDER;
 | 
			
		||||
    dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
    dest->binder = (uintptr_t)obj;
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        dest->hdr.type = BINDER_TYPE_BINDER;
 | 
			
		||||
        dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
        dest->binder = (uintptr_t)obj;
 | 
			
		||||
    } else {
 | 
			
		||||
        dest->hdr.type = BINDER_TYPE_WEAK_BINDER;
 | 
			
		||||
    }
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -153,6 +157,21 @@ GBINDER_IO_FN(encode_remote_object)(
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_fd_object)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    struct flat_binder_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;
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes binder_buffer_object */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
@@ -394,7 +413,7 @@ guint
 | 
			
		||||
GBINDER_IO_FN(decode_buffer_object)(
 | 
			
		||||
    GBinderBuffer* buf,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    GBinderBuffer** out)
 | 
			
		||||
    GBinderIoBufferObject* out)
 | 
			
		||||
{
 | 
			
		||||
    const void* data = (guint8*)buf->data + offset;
 | 
			
		||||
    const gsize size = (offset < buf->size) ? (buf->size - offset) : 0;
 | 
			
		||||
@@ -402,12 +421,36 @@ GBINDER_IO_FN(decode_buffer_object)(
 | 
			
		||||
 | 
			
		||||
    if (size >= sizeof(*flat) && flat->hdr.type == BINDER_TYPE_PTR) {
 | 
			
		||||
        if (out) {
 | 
			
		||||
            *out = gbinder_buffer_new_with_parent(buf,
 | 
			
		||||
                (void*)(uintptr_t)flat->buffer, flat->length);
 | 
			
		||||
            out->data = (void*)(uintptr_t)flat->buffer;
 | 
			
		||||
            out->size = (gsize)flat->length;
 | 
			
		||||
            out->parent_offset = (gsize)flat->parent_offset;
 | 
			
		||||
            out->has_parent = (flat->flags & BINDER_BUFFER_FLAG_HAS_PARENT) ?
 | 
			
		||||
                TRUE : FALSE;
 | 
			
		||||
        }
 | 
			
		||||
        return sizeof(*flat);
 | 
			
		||||
    }
 | 
			
		||||
    if (out) *out = NULL;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(decode_fd_object)(
 | 
			
		||||
    const void* data,
 | 
			
		||||
    gsize size,
 | 
			
		||||
    int* fd)
 | 
			
		||||
{
 | 
			
		||||
    const struct flat_binder_object* obj = data;
 | 
			
		||||
 | 
			
		||||
    if (size >= sizeof(*obj)) {
 | 
			
		||||
        switch (obj->hdr.type) {
 | 
			
		||||
        case BINDER_TYPE_FD:
 | 
			
		||||
            if (fd) *fd = obj->handle;
 | 
			
		||||
            return sizeof(*obj);
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (fd) *fd = -1;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -466,6 +509,7 @@ const GBinderIo GBINDER_IO_PREFIX = {
 | 
			
		||||
    .encode_pointer = GBINDER_IO_FN(encode_pointer),
 | 
			
		||||
    .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_transaction = GBINDER_IO_FN(encode_transaction),
 | 
			
		||||
@@ -478,6 +522,7 @@ const GBinderIo GBINDER_IO_PREFIX = {
 | 
			
		||||
    .decode_binder_ptr_cookie = GBINDER_IO_FN(decode_binder_ptr_cookie),
 | 
			
		||||
    .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),
 | 
			
		||||
 | 
			
		||||
    /* ioctl wrappers */
 | 
			
		||||
    .write_read = GBINDER_IO_FN(write_read)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -43,6 +43,13 @@ typedef struct gbinder_io_buf {
 | 
			
		||||
    gsize consumed;
 | 
			
		||||
} GBinderIoBuf;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_io_buffer_object {
 | 
			
		||||
    void* data;
 | 
			
		||||
    gsize size;
 | 
			
		||||
    gsize parent_offset;
 | 
			
		||||
    gboolean has_parent;
 | 
			
		||||
} GBinderIoBufferObject;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_io_tx_data {
 | 
			
		||||
    int status;
 | 
			
		||||
    guint32 code;
 | 
			
		||||
@@ -131,6 +138,7 @@ struct gbinder_io {
 | 
			
		||||
#define GBINDER_MAX_BINDER_OBJECT_SIZE (24)
 | 
			
		||||
    guint (*encode_local_object)(void* out, GBinderLocalObject* obj);
 | 
			
		||||
    guint (*encode_remote_object)(void* out, GBinderRemoteObject* obj);
 | 
			
		||||
    guint (*encode_fd_object)(void* out, int fd);
 | 
			
		||||
 | 
			
		||||
    /* Encode binder_buffer_object */
 | 
			
		||||
#define GBINDER_MAX_BUFFER_OBJECT_SIZE (40)
 | 
			
		||||
@@ -164,9 +172,10 @@ struct gbinder_io {
 | 
			
		||||
    void* (*decode_binder_ptr_cookie)(const void* data);
 | 
			
		||||
    guint (*decode_cookie)(const void* data, guint64* cookie);
 | 
			
		||||
    guint (*decode_binder_object)(const void* data, gsize size,
 | 
			
		||||
       GBinderObjectRegistry* reg, GBinderRemoteObject** obj);
 | 
			
		||||
        GBinderObjectRegistry* reg, GBinderRemoteObject** obj);
 | 
			
		||||
    guint (*decode_buffer_object)(GBinderBuffer* buf, gsize offset,
 | 
			
		||||
        GBinderBuffer** out);
 | 
			
		||||
        GBinderIoBufferObject* out);
 | 
			
		||||
    guint (*decode_fd_object)(const void* data, gsize size, int* fd);
 | 
			
		||||
 | 
			
		||||
    /* ioctl wrappers */
 | 
			
		||||
    int (*write_read)(int fd, GBinderIoBuf* write, GBinderIoBuf* read);
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -68,39 +68,52 @@ void
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_ipc_new(
 | 
			
		||||
   const char* dev,
 | 
			
		||||
   const GBinderRpcProtocol* protocol);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_ipc_ref(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_unref(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_looper_check(
 | 
			
		||||
   GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
GBinderObjectRegistry*
 | 
			
		||||
gbinder_ipc_object_registry(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_ipc_new_local_object(
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_register_local_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* data);
 | 
			
		||||
    GBinderLocalObject* obj);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_ipc_get_remote_object(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean maybe_dead);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -109,6 +122,7 @@ gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -116,6 +130,7 @@ gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_ipc_transact(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -127,6 +142,7 @@ gbinder_ipc_transact(
 | 
			
		||||
    GDestroyNotify destroy,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_ipc_transact_custom(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -135,23 +151,33 @@ gbinder_ipc_transact_custom(
 | 
			
		||||
    GDestroyNotify destroy,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_cancel(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    gulong id);
 | 
			
		||||
 | 
			
		||||
/* Internal for GBinderLocalObject */
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_local_object_disposed(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderLocalObject* obj);
 | 
			
		||||
 | 
			
		||||
/* Internal for GBinderRemoteObject */
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_remote_object_disposed(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
/* Declared for unit tests */
 | 
			
		||||
G_GNUC_INTERNAL
 | 
			
		||||
__attribute__((destructor))
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_exit(
 | 
			
		||||
    void);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_IPC_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -38,11 +38,13 @@
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
struct gbinder_local_object_priv {
 | 
			
		||||
    GMainContext* context;
 | 
			
		||||
    char* iface;
 | 
			
		||||
    char** ifaces;
 | 
			
		||||
    GBinderLocalTransactFunc txproc;
 | 
			
		||||
    void* user_data;
 | 
			
		||||
};
 | 
			
		||||
@@ -53,6 +55,13 @@ G_DEFINE_TYPE(GBinderLocalObject, gbinder_local_object, G_TYPE_OBJECT)
 | 
			
		||||
    G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_LOCAL_OBJECT, \
 | 
			
		||||
    GBinderLocalObjectClass)
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
(*GBinderLocalObjectTxHandler)(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
enum gbinder_local_object_signal {
 | 
			
		||||
    SIGNAL_WEAK_REFS_CHANGED,
 | 
			
		||||
    SIGNAL_STRONG_REFS_CHANGED,
 | 
			
		||||
@@ -78,6 +87,9 @@ gbinder_local_object_default_can_handle_transaction(
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GBINDER_PING_TRANSACTION:
 | 
			
		||||
    case GBINDER_INTERFACE_TRANSACTION:
 | 
			
		||||
        return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
    case HIDL_PING_TRANSACTION:
 | 
			
		||||
    case HIDL_GET_DESCRIPTOR_TRANSACTION:
 | 
			
		||||
    case HIDL_DESCRIPTOR_CHAIN_TRANSACTION:
 | 
			
		||||
@@ -110,6 +122,39 @@ gbinder_local_object_default_handle_transaction(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_object_ping_transaction(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = gbinder_local_object_io(self);
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("  PING_TRANSACTION");
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_object_interface_transaction(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = gbinder_local_object_io(self);
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("  INTERFACE_TRANSACTION");
 | 
			
		||||
    gbinder_local_reply_append_string16(reply, priv->ifaces[0]);
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_object_hidl_ping_transaction(
 | 
			
		||||
@@ -120,12 +165,10 @@ gbinder_local_object_hidl_ping_transaction(
 | 
			
		||||
    /*android.hidl.base@1.0::IBase interfaceDescriptor() */
 | 
			
		||||
    const GBinderIo* io = gbinder_local_object_io(self);
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("  HIDL_PING_TRANSACTION \"%s\"",
 | 
			
		||||
        gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK);
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
@@ -139,6 +182,7 @@ gbinder_local_object_hidl_get_descriptor_transaction(
 | 
			
		||||
{
 | 
			
		||||
    /*android.hidl.base@1.0::IBase interfaceDescriptor() */
 | 
			
		||||
    const GBinderIo* io = gbinder_local_object_io(self);
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
@@ -146,8 +190,7 @@ gbinder_local_object_hidl_get_descriptor_transaction(
 | 
			
		||||
        gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK);
 | 
			
		||||
    gbinder_writer_append_hidl_string(&writer, self->iface ? self->iface :
 | 
			
		||||
        hidl_base_interface);
 | 
			
		||||
    gbinder_writer_append_hidl_string(&writer, priv->ifaces[0]);
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
@@ -163,17 +206,14 @@ gbinder_local_object_hidl_descriptor_chain_transaction(
 | 
			
		||||
    const GBinderIo* io = gbinder_local_object_io(self);
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(io);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    const char* chain[2];
 | 
			
		||||
    int n = 0;
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("  HIDL_DESCRIPTOR_CHAIN_TRANSACTION \"%s\"",
 | 
			
		||||
        gbinder_remote_request_interface(req));
 | 
			
		||||
    if (self->iface) chain[n++] = self->iface;
 | 
			
		||||
    chain[n++] = hidl_base_interface;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK);
 | 
			
		||||
    gbinder_writer_append_hidl_string_vec(&writer, chain, n);
 | 
			
		||||
    gbinder_writer_append_hidl_string_vec(&writer, (const char**)
 | 
			
		||||
        self->ifaces, -1);
 | 
			
		||||
    *status = GBINDER_STATUS_OK;
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
@@ -187,23 +227,31 @@ gbinder_local_object_default_handle_looper_transaction(
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObjectTxHandler handler = NULL;
 | 
			
		||||
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GBINDER_PING_TRANSACTION:
 | 
			
		||||
        handler = gbinder_local_object_ping_transaction;
 | 
			
		||||
        break;
 | 
			
		||||
    case GBINDER_INTERFACE_TRANSACTION:
 | 
			
		||||
        handler = gbinder_local_object_interface_transaction;
 | 
			
		||||
        break;
 | 
			
		||||
    case HIDL_PING_TRANSACTION:
 | 
			
		||||
        GASSERT(!(flags & GBINDER_TX_FLAG_ONEWAY));
 | 
			
		||||
        return gbinder_local_object_hidl_ping_transaction
 | 
			
		||||
            (self, req, status);
 | 
			
		||||
        handler = gbinder_local_object_hidl_ping_transaction;
 | 
			
		||||
        break;
 | 
			
		||||
    case HIDL_GET_DESCRIPTOR_TRANSACTION:
 | 
			
		||||
        GASSERT(!(flags & GBINDER_TX_FLAG_ONEWAY));
 | 
			
		||||
        return gbinder_local_object_hidl_get_descriptor_transaction
 | 
			
		||||
            (self, req, status);
 | 
			
		||||
        handler = gbinder_local_object_hidl_get_descriptor_transaction;
 | 
			
		||||
        break;
 | 
			
		||||
    case HIDL_DESCRIPTOR_CHAIN_TRANSACTION:
 | 
			
		||||
        GASSERT(!(flags & GBINDER_TX_FLAG_ONEWAY));
 | 
			
		||||
        return gbinder_local_object_hidl_descriptor_chain_transaction
 | 
			
		||||
            (self, req, status);
 | 
			
		||||
        handler = gbinder_local_object_hidl_descriptor_chain_transaction;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        if (status) *status = (-EBADMSG);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    GASSERT(!(flags & GBINDER_TX_FLAG_ONEWAY));
 | 
			
		||||
    return handler(self, req, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -282,20 +330,40 @@ gbinder_local_object_handle_release_proc(
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    void* user_data) /* Since 1.0.30 */
 | 
			
		||||
{
 | 
			
		||||
    /* Should only be called from gbinder_ipc_new_local_local_object() */
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
        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->iface = priv->iface = g_strdup(iface);
 | 
			
		||||
        self->ifaces = (const char**)priv->ifaces;
 | 
			
		||||
        priv->txproc = txproc;
 | 
			
		||||
        priv->user_data = user_data;
 | 
			
		||||
        gbinder_ipc_register_local_object(ipc, self);
 | 
			
		||||
        return self;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -491,7 +559,7 @@ gbinder_local_object_finalize(
 | 
			
		||||
 | 
			
		||||
    GASSERT(!self->strong_refs);
 | 
			
		||||
    gbinder_ipc_unref(self->ipc);
 | 
			
		||||
    g_free(priv->iface);
 | 
			
		||||
    g_strfreev(priv->ifaces);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_local_object_parent_class)->finalize(local);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -55,7 +55,7 @@ struct gbinder_local_object {
 | 
			
		||||
    GObject object;
 | 
			
		||||
    GBinderLocalObjectPriv* priv;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    const char* const* ifaces;
 | 
			
		||||
    gint weak_refs;
 | 
			
		||||
    gint strong_refs;
 | 
			
		||||
};
 | 
			
		||||
@@ -87,14 +87,6 @@ GType gbinder_local_object_get_type(void);
 | 
			
		||||
#define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
 | 
			
		||||
#define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
 | 
			
		||||
 | 
			
		||||
/* Should only be called from gbinder_ipc_new_local_object() */
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_local_object_add_weak_refs_changed_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
 
 | 
			
		||||
@@ -95,14 +95,13 @@ gbinder_local_reply_new(
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects)
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = gbinder_buffer_io(buffer);
 | 
			
		||||
    GBinderLocalReply* self = gbinder_local_reply_new(io);
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer, objects);
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,7 @@ gbinder_local_reply_data(
 | 
			
		||||
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_local_reply_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects);
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_LOCAL_REPLY_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,6 @@ struct gbinder_local_request {
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    GBinderWriterData data;
 | 
			
		||||
    GBinderOutputData out;
 | 
			
		||||
    GBinderBufferMemory* memory;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
@@ -106,14 +105,13 @@ gbinder_local_request_new(
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects)
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* self = gbinder_local_request_new
 | 
			
		||||
        (gbinder_buffer_io(buffer), NULL);
 | 
			
		||||
 | 
			
		||||
    if (self) {
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer, objects);
 | 
			
		||||
        gbinder_writer_data_set_contents(&self->data, buffer);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
@@ -128,7 +126,6 @@ gbinder_local_request_free(
 | 
			
		||||
    g_byte_array_free(data->bytes, TRUE);
 | 
			
		||||
    gutil_int_array_free(data->offsets, TRUE);
 | 
			
		||||
    gbinder_cleanup_free(data->cleanup);
 | 
			
		||||
    gbinder_buffer_memory_unref(self->memory);
 | 
			
		||||
    g_slice_free(GBinderLocalRequest, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,8 +48,7 @@ gbinder_local_request_data(
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_local_request_new_from_data(
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects);
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_LOCAL_REQUEST_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -38,6 +38,9 @@
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_reader_priv {
 | 
			
		||||
    const guint8* start;
 | 
			
		||||
    const guint8* end;
 | 
			
		||||
@@ -50,6 +53,8 @@ G_STATIC_ASSERT(sizeof(GBinderReader) >= sizeof(GBinderReaderPriv));
 | 
			
		||||
 | 
			
		||||
static inline GBinderReaderPriv* gbinder_reader_cast(GBinderReader* reader)
 | 
			
		||||
    { return (GBinderReaderPriv*)reader; }
 | 
			
		||||
static inline const GBinderReaderPriv* gbinder_reader_cast_c
 | 
			
		||||
    (const GBinderReader* reader)  { return (GBinderReaderPriv*)reader; }
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_reader_init(
 | 
			
		||||
@@ -81,9 +86,9 @@ gbinder_reader_init(
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_at_end(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
    const GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->ptr >= p->end;
 | 
			
		||||
}
 | 
			
		||||
@@ -229,16 +234,67 @@ gbinder_reader_read_double(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
inline
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_can_read_object(
 | 
			
		||||
    GBinderReaderPriv* p)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderData* data = p->data;
 | 
			
		||||
 | 
			
		||||
    return data && data->reg &&
 | 
			
		||||
        p->objects && p->objects[0] &&
 | 
			
		||||
        p->ptr == p->objects[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_reader_read_fd(
 | 
			
		||||
    GBinderReader* reader) /* Since 1.0.18 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
 | 
			
		||||
    if (gbinder_reader_can_read_object(p)) {
 | 
			
		||||
        int fd;
 | 
			
		||||
        const guint eaten = p->data->reg->io->decode_fd_object(p->ptr,
 | 
			
		||||
            gbinder_reader_bytes_remaining(reader), &fd);
 | 
			
		||||
 | 
			
		||||
        if (eaten) {
 | 
			
		||||
            GASSERT(fd >= 0);
 | 
			
		||||
            p->ptr += eaten;
 | 
			
		||||
            p->objects++;
 | 
			
		||||
            return fd;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_reader_read_dup_fd(
 | 
			
		||||
    GBinderReader* reader) /* Since 1.0.18 */
 | 
			
		||||
{
 | 
			
		||||
    const int fd = gbinder_reader_read_fd(reader);
 | 
			
		||||
 | 
			
		||||
    if (fd >= 0) {
 | 
			
		||||
        const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
 | 
			
		||||
 | 
			
		||||
        if (dupfd >= 0) {
 | 
			
		||||
            return dupfd;
 | 
			
		||||
        } else {
 | 
			
		||||
            GWARN("Error dupping fd %d: %s", fd, strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_nullable_object(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    GBinderRemoteObject** out)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
    const GBinderReaderData* data = p->data;
 | 
			
		||||
 | 
			
		||||
    if (data && data->reg && p->objects && p->objects[0] &&
 | 
			
		||||
        p->ptr == p->objects[0]) {
 | 
			
		||||
    if (gbinder_reader_can_read_object(p)) {
 | 
			
		||||
        const GBinderReaderData* data = p->data;
 | 
			
		||||
        const guint eaten = data->reg->io->decode_binder_object(p->ptr,
 | 
			
		||||
            gbinder_reader_bytes_remaining(reader), data->reg, out);
 | 
			
		||||
 | 
			
		||||
@@ -264,15 +320,14 @@ gbinder_reader_read_object(
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_buffer_impl(
 | 
			
		||||
gbinder_reader_read_buffer_object(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    GBinderBuffer** out)
 | 
			
		||||
    GBinderIoBufferObject* out)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
    const GBinderReaderData* data = p->data;
 | 
			
		||||
 | 
			
		||||
    if (data && data->reg && p->objects && p->objects[0] &&
 | 
			
		||||
        p->ptr == p->objects[0]) {
 | 
			
		||||
    if (gbinder_reader_can_read_object(p)) {
 | 
			
		||||
        const GBinderReaderData* data = p->data;
 | 
			
		||||
        GBinderBuffer* buf = data->buffer;
 | 
			
		||||
        const GBinderIo* io = data->reg->io;
 | 
			
		||||
        const gsize offset = p->ptr - (guint8*)buf->data;
 | 
			
		||||
@@ -284,7 +339,6 @@ gbinder_reader_read_buffer_impl(
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (out) *out = NULL;
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -292,34 +346,36 @@ GBinderBuffer*
 | 
			
		||||
gbinder_reader_read_buffer(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBuffer* buf = NULL;
 | 
			
		||||
    GBinderIoBufferObject obj;
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_read_buffer_impl(reader, &buf);
 | 
			
		||||
    return buf;
 | 
			
		||||
    if (gbinder_reader_read_buffer_object(reader, &obj)) {
 | 
			
		||||
        const GBinderReaderData* data = gbinder_reader_cast(reader)->data;
 | 
			
		||||
        GBinderBuffer* buf = data->buffer;
 | 
			
		||||
 | 
			
		||||
        return gbinder_buffer_new_with_parent(buf, obj.data, obj.size);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_skip_buffer(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_reader_read_buffer_impl(reader, NULL);
 | 
			
		||||
    return gbinder_reader_read_buffer_object(reader, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper for gbinder_reader_read_hidl_struct() macro */
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_read_hidl_struct1(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize size) /* since 1.0.9 */
 | 
			
		||||
    gsize size) /* Since 1.0.9 */
 | 
			
		||||
{
 | 
			
		||||
    const void* result = NULL;
 | 
			
		||||
    GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
    GBinderIoBufferObject obj;
 | 
			
		||||
 | 
			
		||||
    /* Check the size */
 | 
			
		||||
    if (buf && buf->size == size) {
 | 
			
		||||
        result = buf->data;
 | 
			
		||||
    if (gbinder_reader_read_buffer_object(reader, &obj) && obj.size == size) {
 | 
			
		||||
        return obj.data;
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    return result;
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Doesn't copy the data */
 | 
			
		||||
@@ -329,30 +385,28 @@ gbinder_reader_read_hidl_vec(
 | 
			
		||||
    gsize* count,
 | 
			
		||||
    gsize* elemsize)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
    gsize out_count = 0, out_elemsize = 0;
 | 
			
		||||
    GBinderIoBufferObject obj;
 | 
			
		||||
    const void* out = NULL;
 | 
			
		||||
    gsize out_count = 0, out_elemsize = 0;
 | 
			
		||||
 | 
			
		||||
    if (buf && buf->size == sizeof(HidlVec)) {
 | 
			
		||||
        const HidlVec* vec = buf->data;
 | 
			
		||||
    if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
        obj.data && obj.size == sizeof(GBinderHidlVec)) {
 | 
			
		||||
        const GBinderHidlVec* vec = obj.data;
 | 
			
		||||
        const void* next = vec->data.ptr;
 | 
			
		||||
 | 
			
		||||
        if (next) {
 | 
			
		||||
            GBinderBuffer* vbuf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
 | 
			
		||||
            if (vbuf && vbuf->data == next && ((!vec->count && !vbuf->size) ||
 | 
			
		||||
                (vec->count && vbuf->size && !(vbuf->size % vec->count)))) {
 | 
			
		||||
                out_elemsize = vec->count ? (vbuf->size / vec->count) : 0;
 | 
			
		||||
            if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
                obj.data == next && ((!vec->count && !obj.size) ||
 | 
			
		||||
                (vec->count && obj.size && !(obj.size % vec->count)))) {
 | 
			
		||||
                out_elemsize = vec->count ? (obj.size / vec->count) : 0;
 | 
			
		||||
                out_count = vec->count;
 | 
			
		||||
                out = vbuf->data;
 | 
			
		||||
                out = obj.data;
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_buffer_free(vbuf);
 | 
			
		||||
        } else if (!vec->count) {
 | 
			
		||||
            /* Any non-NULL pointer just to indicate success */
 | 
			
		||||
            /* Any non-NULL pointer just to indicate success? */
 | 
			
		||||
            out = vec;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    if (elemsize) {
 | 
			
		||||
        *elemsize = out_elemsize;
 | 
			
		||||
    }
 | 
			
		||||
@@ -367,7 +421,7 @@ const void*
 | 
			
		||||
gbinder_reader_read_hidl_vec1(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* count,
 | 
			
		||||
    guint expected_elem_size) /* since 1.0.9 */
 | 
			
		||||
    guint expected_elem_size) /* Since 1.0.9 */
 | 
			
		||||
{
 | 
			
		||||
    gsize actual;
 | 
			
		||||
    const void* data = gbinder_reader_read_hidl_vec(reader, count, &actual);
 | 
			
		||||
@@ -376,88 +430,94 @@ gbinder_reader_read_hidl_vec1(
 | 
			
		||||
    return (data && (actual == expected_elem_size || !actual)) ? data : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_reader_read_hidl_string_c(
 | 
			
		||||
    GBinderReader* reader) /* Since 1.0.23 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderIoBufferObject obj;
 | 
			
		||||
 | 
			
		||||
    if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
        obj.data && obj.size == sizeof(GBinderHidlString)) {
 | 
			
		||||
        const GBinderHidlString* str = obj.data;
 | 
			
		||||
 | 
			
		||||
        if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
            obj.has_parent &&
 | 
			
		||||
            obj.parent_offset == GBINDER_HIDL_STRING_BUFFER_OFFSET &&
 | 
			
		||||
            obj.data == str->data.str &&
 | 
			
		||||
            obj.size == str->len + 1 &&
 | 
			
		||||
            str->data.str[str->len] == 0) {
 | 
			
		||||
            return str->data.str;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char*
 | 
			
		||||
gbinder_reader_read_hidl_string(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
    char* str = NULL;
 | 
			
		||||
 | 
			
		||||
    if (buf && buf->size == sizeof(HidlString)) {
 | 
			
		||||
        const HidlString* s = buf->data;
 | 
			
		||||
        GBinderBuffer* sbuf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
 | 
			
		||||
        if (sbuf && sbuf->size == s->len + 1 &&
 | 
			
		||||
            sbuf->data == s->data.str &&
 | 
			
		||||
            s->data.str[s->len] == 0) {
 | 
			
		||||
            str = g_strdup(s->data.str);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_buffer_free(sbuf);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    return str;
 | 
			
		||||
    /* This function should've been called gbinder_reader_dup_hidl_string */
 | 
			
		||||
    return g_strdup(gbinder_reader_read_hidl_string_c(reader));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char**
 | 
			
		||||
gbinder_reader_read_hidl_string_vec(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
    GBinderIoBufferObject obj;
 | 
			
		||||
 | 
			
		||||
    /* First buffer contains hidl_vector */
 | 
			
		||||
    if (buf && buf->size == sizeof(HidlVec)) {
 | 
			
		||||
        HidlVec* vec = buf->data;
 | 
			
		||||
    if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
        obj.data && obj.size == sizeof(GBinderHidlVec)) {
 | 
			
		||||
        GBinderHidlVec* vec = obj.data;
 | 
			
		||||
        const guint n = vec->count;
 | 
			
		||||
        const void* next = vec->data.ptr;
 | 
			
		||||
 | 
			
		||||
        gbinder_buffer_free(buf);
 | 
			
		||||
        if (!next && !n) {
 | 
			
		||||
            char** out = g_new(char*, 1);
 | 
			
		||||
            /* Should this be considered an error? */
 | 
			
		||||
            return g_new0(char*, 1);
 | 
			
		||||
        } else if (gbinder_reader_read_buffer_object(reader, &obj) &&
 | 
			
		||||
                   /* The second buffer (if any) contains n hidl_string's */
 | 
			
		||||
                   obj.parent_offset == GBINDER_HIDL_VEC_BUFFER_OFFSET &&
 | 
			
		||||
                   obj.has_parent &&
 | 
			
		||||
                   obj.data == next &&
 | 
			
		||||
                   obj.size == (sizeof(GBinderHidlString) * n)) {
 | 
			
		||||
            const GBinderHidlString* strings = obj.data;
 | 
			
		||||
            GPtrArray* list = g_ptr_array_sized_new(n + 1);
 | 
			
		||||
            guint i;
 | 
			
		||||
 | 
			
		||||
            out[0] = NULL;
 | 
			
		||||
            return out;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* The second buffer (if any) contains n hidl_string's */
 | 
			
		||||
            buf = gbinder_reader_read_buffer(reader);
 | 
			
		||||
            if (buf && buf->data == next && buf->size == sizeof(HidlString)*n) {
 | 
			
		||||
                const HidlString* strings = buf->data;
 | 
			
		||||
                GBinderBuffer* sbuf;
 | 
			
		||||
                GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
                guint i;
 | 
			
		||||
            /* Now we expect n buffers containing the actual data */
 | 
			
		||||
            for (i = 0; i < n &&
 | 
			
		||||
                gbinder_reader_read_buffer_object(reader, &obj); i++) {
 | 
			
		||||
                const GBinderHidlString* s = strings + i;
 | 
			
		||||
                const gsize expected_offset = (i * sizeof(*s)) +
 | 
			
		||||
                    GBINDER_HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
                if (obj.has_parent &&
 | 
			
		||||
                    obj.parent_offset == expected_offset &&
 | 
			
		||||
                    obj.data == s->data.str &&
 | 
			
		||||
                    obj.size == s->len + 1 &&
 | 
			
		||||
                    s->data.str[s->len] == 0) {
 | 
			
		||||
                    char* name = g_strdup(s->data.str);
 | 
			
		||||
 | 
			
		||||
                /* Now we expect n buffers containing the actual data */
 | 
			
		||||
                for (i=0; i<n &&
 | 
			
		||||
                    (sbuf = gbinder_reader_read_buffer(reader)); i++) {
 | 
			
		||||
                    const HidlString* s = strings + i;
 | 
			
		||||
                    if (sbuf->size == s->len + 1 &&
 | 
			
		||||
                        sbuf->data == s->data.str &&
 | 
			
		||||
                        s->data.str[s->len] == 0) {
 | 
			
		||||
                        char* name = g_strdup(s->data.str);
 | 
			
		||||
 | 
			
		||||
                        g_ptr_array_add(list, name);
 | 
			
		||||
                        GVERBOSE_("%u. %s", i + 1, name);
 | 
			
		||||
                        gbinder_buffer_free(sbuf);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        GWARN("Unexpected hidl_string buffer %p/%u vs %p/%u",
 | 
			
		||||
                            sbuf->data, (guint)sbuf->size, s->data.str, s->len);
 | 
			
		||||
                        gbinder_buffer_free(sbuf);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    g_ptr_array_add(list, name);
 | 
			
		||||
                    GVERBOSE_("%u. %s", i + 1, name);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GWARN("Unexpected hidl_string buffer %p/%u vs %p/%u",
 | 
			
		||||
                        obj.data, (guint)obj.size, s->data.str, s->len);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (i == n) {
 | 
			
		||||
                    gbinder_buffer_free(buf);
 | 
			
		||||
                    g_ptr_array_add(list, NULL);
 | 
			
		||||
                    return (char**)g_ptr_array_free(list, FALSE);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                g_ptr_array_set_free_func(list, g_free);
 | 
			
		||||
                g_ptr_array_free(list, TRUE);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (i == n) {
 | 
			
		||||
                g_ptr_array_add(list, NULL);
 | 
			
		||||
                return (char**)g_ptr_array_free(list, FALSE);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            g_ptr_array_set_free_func(list, g_free);
 | 
			
		||||
            g_ptr_array_free(list, TRUE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    GWARN("Invalid hidl_vec<string>");
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -489,6 +549,24 @@ gboolean
 | 
			
		||||
gbinder_reader_read_nullable_string16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    char** out)
 | 
			
		||||
{
 | 
			
		||||
    const gunichar2* str;
 | 
			
		||||
    gsize len;
 | 
			
		||||
 | 
			
		||||
    if (gbinder_reader_read_nullable_string16_utf16(reader, &str, &len)) {
 | 
			
		||||
        if (out) {
 | 
			
		||||
            *out = str ? g_utf16_to_utf8(str, len, NULL, NULL, NULL) : NULL;
 | 
			
		||||
        }
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_nullable_string16_utf16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    const gunichar2** out,
 | 
			
		||||
    gsize* out_len) /* Since 1.0.17 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
 | 
			
		||||
@@ -502,15 +580,21 @@ gbinder_reader_read_nullable_string16(
 | 
			
		||||
            if (out) {
 | 
			
		||||
                *out = NULL;
 | 
			
		||||
            }
 | 
			
		||||
            if (out_len) {
 | 
			
		||||
                *out_len = 0;
 | 
			
		||||
            }
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else if (len >= 0) {
 | 
			
		||||
            const guint32 padded_len = G_ALIGN4((len+1)*2);
 | 
			
		||||
            const gunichar2* utf16 = (const gunichar2*)(p->ptr + 4);
 | 
			
		||||
            const guint32 padded_len = G_ALIGN4((len + 1)*2);
 | 
			
		||||
            const gunichar2* utf16 = (gunichar2*)(p->ptr + 4);
 | 
			
		||||
 | 
			
		||||
            if ((p->ptr + padded_len + 4) <= p->end) {
 | 
			
		||||
                p->ptr += padded_len + 4;
 | 
			
		||||
                if (out) {
 | 
			
		||||
                    *out = g_utf16_to_utf8(utf16, len, NULL, NULL, NULL);
 | 
			
		||||
                    *out = utf16;
 | 
			
		||||
                }
 | 
			
		||||
                if (out_len) {
 | 
			
		||||
                    *out_len = len;
 | 
			
		||||
                }
 | 
			
		||||
                return TRUE;
 | 
			
		||||
            }
 | 
			
		||||
@@ -519,6 +603,21 @@ gbinder_reader_read_nullable_string16(
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const gunichar2*
 | 
			
		||||
gbinder_reader_read_string16_utf16(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* len) /* Since 1.0.26 */
 | 
			
		||||
{
 | 
			
		||||
    const gunichar2* str;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Use gbinder_reader_read_nullable_string16_utf16 to distinguish
 | 
			
		||||
     * NULL string from a parsing failure.
 | 
			
		||||
     */
 | 
			
		||||
    return gbinder_reader_read_nullable_string16_utf16(reader, &str, len) ?
 | 
			
		||||
        str : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char*
 | 
			
		||||
gbinder_reader_read_string16(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
@@ -555,24 +654,59 @@ gbinder_reader_skip_string16(
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_read(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
const void*
 | 
			
		||||
gbinder_reader_read_byte_array(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    gsize* len) /* Since 1.0.12 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
    const void* data = NULL;
 | 
			
		||||
    const gint32* ptr;
 | 
			
		||||
    *len = 0;
 | 
			
		||||
 | 
			
		||||
    if (gbinder_reader_can_read(p, sizeof(*ptr))) {
 | 
			
		||||
        ptr = (void*)p->ptr;
 | 
			
		||||
        if (*ptr <= 0) {
 | 
			
		||||
            p->ptr += sizeof(*ptr);
 | 
			
		||||
            /* Any non-NULL pointer just to indicate success */
 | 
			
		||||
            data = p->start;
 | 
			
		||||
        } else if (gbinder_reader_can_read(p, sizeof(*ptr) + *ptr)) {
 | 
			
		||||
            *len = (gsize)*ptr;
 | 
			
		||||
            p->ptr += sizeof(*ptr);
 | 
			
		||||
            data = p->ptr;
 | 
			
		||||
            p->ptr += *len;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_read(
 | 
			
		||||
    const GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->ptr - p->start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_reader_bytes_remaining(
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
    const GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderPriv* p = gbinder_reader_cast(reader);
 | 
			
		||||
    const GBinderReaderPriv* p = gbinder_reader_cast_c(reader);
 | 
			
		||||
 | 
			
		||||
    return p->end - p->ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_reader_copy(
 | 
			
		||||
    GBinderReader* dest,
 | 
			
		||||
    const GBinderReader* src)
 | 
			
		||||
{
 | 
			
		||||
    /* It's actually quite simple :) */
 | 
			
		||||
    memcpy(dest, src, sizeof(*dest));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -33,6 +33,7 @@
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
struct gbinder_remote_object_priv {
 | 
			
		||||
@@ -65,9 +66,20 @@ void
 | 
			
		||||
gbinder_remote_object_died_on_main_thread(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = self->ipc;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!self->dead);
 | 
			
		||||
    self->dead = TRUE;
 | 
			
		||||
    g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
 | 
			
		||||
    if (!self->dead) {
 | 
			
		||||
        self->dead = TRUE;
 | 
			
		||||
        /* 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_driver_clear_death_notification(driver, self);
 | 
			
		||||
        gbinder_driver_release(driver, self->handle);
 | 
			
		||||
        g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -79,6 +91,47 @@ gbinder_remote_object_died_handle(
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internal interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_object_reanimate(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * Don't try to reanimate those who hasn't died yet. Reanimation is
 | 
			
		||||
     * kind of a special case and should only be used for servicemanager
 | 
			
		||||
     * objects.
 | 
			
		||||
     */
 | 
			
		||||
    if (self->dead) {
 | 
			
		||||
        GBinderIpc* ipc = self->ipc;
 | 
			
		||||
        GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
 | 
			
		||||
        /* Kick the horse */
 | 
			
		||||
        GASSERT(self->handle == GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
        if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
 | 
			
		||||
            /* Wow, it's alive! */
 | 
			
		||||
            self->dead = FALSE;
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, self->handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return !self->dead;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_handle_death_notification(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    /* This function is invoked from the looper thread, the caller has
 | 
			
		||||
     * checked the object pointer */
 | 
			
		||||
    GVERBOSE_("%p %u", self, self->handle);
 | 
			
		||||
    g_main_context_invoke_full(self->priv->context, G_PRIORITY_DEFAULT,
 | 
			
		||||
        gbinder_remote_object_died_handle, gbinder_remote_object_ref(self),
 | 
			
		||||
        g_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -86,15 +139,19 @@ gbinder_remote_object_died_handle(
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_remote_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean dead)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(ipc) && gbinder_driver_acquire(ipc->driver, handle)) {
 | 
			
		||||
    if (G_LIKELY(ipc)) {
 | 
			
		||||
        GBinderRemoteObject* self = g_object_new
 | 
			
		||||
            (GBINDER_TYPE_REMOTE_OBJECT, NULL);
 | 
			
		||||
 | 
			
		||||
        self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
        self->handle = handle;
 | 
			
		||||
        gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        if (!(self->dead = dead)) {
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        }
 | 
			
		||||
        return self;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -121,6 +178,13 @@ gbinder_remote_object_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_remote_object_ipc(
 | 
			
		||||
    GBinderRemoteObject* self) /* Since 1.0.30 */
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->ipc : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_object_is_dead(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
@@ -152,18 +216,6 @@ gbinder_remote_object_remove_handler(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_handle_death_notification(
 | 
			
		||||
    GBinderRemoteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    /* This function is invoked from the looper thread, the caller has
 | 
			
		||||
     * checked the object pointer */
 | 
			
		||||
    GVERBOSE_("%p %u", self, self->handle);
 | 
			
		||||
    g_main_context_invoke_full(self->priv->context, G_PRIORITY_DEFAULT,
 | 
			
		||||
        gbinder_remote_object_died_handle, gbinder_remote_object_ref(self),
 | 
			
		||||
        g_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -200,8 +252,10 @@ gbinder_remote_object_finalize(
 | 
			
		||||
    GBinderIpc* ipc = self->ipc;
 | 
			
		||||
    GBinderDriver* driver = ipc->driver;
 | 
			
		||||
 | 
			
		||||
    gbinder_driver_clear_death_notification(driver, self);
 | 
			
		||||
    gbinder_driver_release(driver, self->handle);
 | 
			
		||||
    if (!self->dead) {
 | 
			
		||||
        gbinder_driver_clear_death_notification(driver, self);
 | 
			
		||||
        gbinder_driver_release(driver, self->handle);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    G_OBJECT_CLASS(gbinder_remote_object_parent_class)->finalize(remote);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -54,7 +54,12 @@ struct gbinder_remote_object {
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_remote_object_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle);
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    gboolean maybe_dead);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_object_reanimate(
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_object_handle_death_notification(
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
#include "gbinder_local_reply_p.h"
 | 
			
		||||
#include "gbinder_reader_p.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_buffer.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
@@ -65,26 +65,22 @@ gbinder_remote_reply_free(
 | 
			
		||||
 | 
			
		||||
    gbinder_object_registry_unref(data->reg);
 | 
			
		||||
    gbinder_buffer_free(data->buffer);
 | 
			
		||||
    g_free(data->objects);
 | 
			
		||||
    g_slice_free(GBinderRemoteReply, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_reply_set_data(
 | 
			
		||||
    GBinderRemoteReply* self,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects)
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* data = &self->data;
 | 
			
		||||
 | 
			
		||||
        g_free(data->objects);
 | 
			
		||||
        gbinder_buffer_free(data->buffer);
 | 
			
		||||
        data->buffer = buffer;
 | 
			
		||||
        data->objects = objects;
 | 
			
		||||
        data->objects = gbinder_buffer_objects(buffer);
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_buffer_free(buffer);
 | 
			
		||||
        g_free(objects);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -125,7 +121,7 @@ gbinder_remote_reply_copy_to_local(
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* d = &self->data;
 | 
			
		||||
 | 
			
		||||
        return gbinder_local_reply_new_from_data(d->buffer, d->objects);
 | 
			
		||||
        return gbinder_local_reply_new_from_data(d->buffer);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -44,8 +44,7 @@ gbinder_remote_reply_new(
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_reply_set_data(
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects);
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_reply_is_empty(
 | 
			
		||||
 
 | 
			
		||||
@@ -35,12 +35,15 @@
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
#include "gbinder_buffer.h"
 | 
			
		||||
#include "gbinder_buffer_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
struct gbinder_remote_request {
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_remote_request_priv {
 | 
			
		||||
    GBinderRemoteRequest pub;
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    pid_t pid;
 | 
			
		||||
    uid_t euid;
 | 
			
		||||
@@ -49,7 +52,11 @@ struct gbinder_remote_request {
 | 
			
		||||
    char* iface2;
 | 
			
		||||
    gsize header_size;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
};
 | 
			
		||||
} GBinderRemoteRequestPriv;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC GBinderRemoteRequestPriv*
 | 
			
		||||
gbinder_remote_request_cast(GBinderRemoteRequest* pub)
 | 
			
		||||
    { return G_LIKELY(pub) ? G_CAST(pub,GBinderRemoteRequestPriv,pub) : NULL; }
 | 
			
		||||
 | 
			
		||||
GBinderRemoteRequest*
 | 
			
		||||
gbinder_remote_request_new(
 | 
			
		||||
@@ -58,7 +65,7 @@ gbinder_remote_request_new(
 | 
			
		||||
    pid_t pid,
 | 
			
		||||
    uid_t euid)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequest* self = g_slice_new0(GBinderRemoteRequest);
 | 
			
		||||
    GBinderRemoteRequestPriv* self = g_slice_new0(GBinderRemoteRequestPriv);
 | 
			
		||||
    GBinderReaderData* data = &self->data;
 | 
			
		||||
 | 
			
		||||
    g_atomic_int_set(&self->refcount, 1);
 | 
			
		||||
@@ -66,17 +73,19 @@ gbinder_remote_request_new(
 | 
			
		||||
    self->euid = euid;
 | 
			
		||||
    self->protocol = protocol;
 | 
			
		||||
    data->reg = gbinder_object_registry_ref(reg);
 | 
			
		||||
    return self;
 | 
			
		||||
    return &self->pub;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
gbinder_remote_request_copy_to_local(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* d = &self->data;
 | 
			
		||||
 | 
			
		||||
        return gbinder_local_request_new_from_data(d->buffer, d->objects);
 | 
			
		||||
        return gbinder_local_request_new_from_data(d->buffer);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -84,22 +93,27 @@ gbinder_remote_request_copy_to_local(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_free(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequestPriv* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderReaderData* data = &self->data;
 | 
			
		||||
    GBinderRemoteRequest* req = &self->pub;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!req->tx);
 | 
			
		||||
    if (req->tx) {
 | 
			
		||||
        GWARN("Request is dropped without completing the transaction");
 | 
			
		||||
        gbinder_remote_request_complete(req, NULL, -ECANCELED);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_object_registry_unref(data->reg);
 | 
			
		||||
    gbinder_buffer_free(data->buffer);
 | 
			
		||||
    g_free(data->objects);
 | 
			
		||||
    g_free(self->iface2);
 | 
			
		||||
    g_slice_free(GBinderRemoteRequest, self);
 | 
			
		||||
    g_slice_free(GBinderRemoteRequestPriv, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
inline
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_init_reader2(
 | 
			
		||||
    GBinderRemoteRequest* self,
 | 
			
		||||
    GBinderRemoteRequestPriv* self,
 | 
			
		||||
    GBinderReader* p)
 | 
			
		||||
{
 | 
			
		||||
    /* The caller has already checked the request for NULL */
 | 
			
		||||
@@ -116,53 +130,64 @@ gbinder_remote_request_init_reader2(
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_set_data(
 | 
			
		||||
    GBinderRemoteRequest* self,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects)
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReaderData* data = &self->data;
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
        g_free(self->iface2);
 | 
			
		||||
        g_free(data->objects);
 | 
			
		||||
        gbinder_buffer_free(data->buffer);
 | 
			
		||||
        data->buffer = buffer;
 | 
			
		||||
        data->objects = objects;
 | 
			
		||||
        data->objects = gbinder_buffer_objects(buffer);
 | 
			
		||||
 | 
			
		||||
        /* Parse RPC header */
 | 
			
		||||
        self->header_size = 0;
 | 
			
		||||
        gbinder_remote_request_init_reader2(self, &reader);
 | 
			
		||||
        self->iface = self->protocol->read_rpc_header(&reader, &self->iface2);
 | 
			
		||||
        self->header_size = gbinder_reader_bytes_read(&reader);
 | 
			
		||||
        self->iface = self->protocol->read_rpc_header(&reader, txcode,
 | 
			
		||||
            &self->iface2);
 | 
			
		||||
        if (self->iface) {
 | 
			
		||||
            self->header_size = gbinder_reader_bytes_read(&reader);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* No RPC header */
 | 
			
		||||
            self->header_size = 0;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_buffer_free(buffer);
 | 
			
		||||
        g_free(objects);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_remote_request_interface(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    return G_LIKELY(self) ? self->iface : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteRequest*
 | 
			
		||||
gbinder_remote_request_ref(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
        g_atomic_int_inc(&self->refcount);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_unref(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GASSERT(self->refcount > 0);
 | 
			
		||||
        if (g_atomic_int_dec_and_test(&self->refcount)) {
 | 
			
		||||
@@ -173,9 +198,11 @@ gbinder_remote_request_unref(
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_init_reader(
 | 
			
		||||
    GBinderRemoteRequest* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    GBinderReader* reader)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        gbinder_remote_request_init_reader2(self, reader);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -185,15 +212,19 @@ gbinder_remote_request_init_reader(
 | 
			
		||||
 | 
			
		||||
pid_t
 | 
			
		||||
gbinder_remote_request_sender_pid(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    return G_LIKELY(self) ? self->pid : (uid_t)(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uid_t
 | 
			
		||||
gbinder_remote_request_sender_euid(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    return G_LIKELY(self) ? self->euid : (uid_t)(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -207,9 +238,11 @@ gbinder_remote_request_read_int32(
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_request_read_uint32(
 | 
			
		||||
    GBinderRemoteRequest* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint32* value)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
@@ -229,9 +262,11 @@ gbinder_remote_request_read_int64(
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_remote_request_read_uint64(
 | 
			
		||||
    GBinderRemoteRequest* self,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint64* value)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
@@ -243,8 +278,10 @@ gbinder_remote_request_read_uint64(
 | 
			
		||||
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_remote_request_read_string8(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
@@ -256,8 +293,10 @@ gbinder_remote_request_read_string8(
 | 
			
		||||
 | 
			
		||||
char*
 | 
			
		||||
gbinder_remote_request_read_string16(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
@@ -269,8 +308,10 @@ gbinder_remote_request_read_string16(
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
gbinder_remote_request_read_object(
 | 
			
		||||
    GBinderRemoteRequest* self)
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -37,6 +37,10 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
struct gbinder_remote_request {
 | 
			
		||||
    GBinderIpcLooperTx* tx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBinderRemoteRequest*
 | 
			
		||||
gbinder_remote_request_new(
 | 
			
		||||
    GBinderObjectRegistry* reg,
 | 
			
		||||
@@ -47,8 +51,8 @@ gbinder_remote_request_new(
 | 
			
		||||
void
 | 
			
		||||
gbinder_remote_request_set_data(
 | 
			
		||||
    GBinderRemoteRequest* request,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects);
 | 
			
		||||
    guint txcode,
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_REMOTE_REQUEST_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -50,6 +50,14 @@
 | 
			
		||||
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
 | 
			
		||||
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_binder_write_ping(
 | 
			
		||||
    GBinderWriter* writer)
 | 
			
		||||
{
 | 
			
		||||
    /* No payload */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_binder_write_rpc_header(
 | 
			
		||||
@@ -69,9 +77,13 @@ static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_binder_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
{
 | 
			
		||||
    if (gbinder_reader_read_int32(reader, NULL)) {
 | 
			
		||||
    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)) {
 | 
			
		||||
        *iface = gbinder_reader_read_string16(reader);
 | 
			
		||||
    } else {
 | 
			
		||||
        *iface = NULL;
 | 
			
		||||
@@ -95,10 +107,20 @@ gbinder_rpc_protocol_hwbinder_write_rpc_header(
 | 
			
		||||
    gbinder_writer_append_string8(writer, iface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_rpc_protocol_hwbinder_write_ping(
 | 
			
		||||
    GBinderWriter* writer)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_rpc_protocol_hwbinder_write_rpc_header(writer,
 | 
			
		||||
        "android.hidl.base@1.0::IBase");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
const char*
 | 
			
		||||
gbinder_rpc_protocol_hwbinder_read_rpc_header(
 | 
			
		||||
    GBinderReader* reader,
 | 
			
		||||
    guint32 txcode,
 | 
			
		||||
    char** iface)
 | 
			
		||||
{
 | 
			
		||||
    *iface = NULL;
 | 
			
		||||
@@ -110,13 +132,17 @@ gbinder_rpc_protocol_hwbinder_read_rpc_header(
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
const GBinderRpcProtocol gbinder_rpc_protocol_binder = {
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_binder_read_rpc_header,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_binder_write_rpc_header
 | 
			
		||||
    .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 = {
 | 
			
		||||
    .read_rpc_header = gbinder_rpc_protocol_hwbinder_read_rpc_header,
 | 
			
		||||
    .write_rpc_header = gbinder_rpc_protocol_hwbinder_write_rpc_header
 | 
			
		||||
    .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*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -42,8 +42,11 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct gbinder_rpc_protocol {
 | 
			
		||||
    const char* (*read_rpc_header)(GBinderReader* reader, char** iface);
 | 
			
		||||
    guint32 ping_tx;
 | 
			
		||||
    void (*write_ping)(GBinderWriter* writer);
 | 
			
		||||
    void (*write_rpc_header)(GBinderWriter* writer, const char* iface);
 | 
			
		||||
    const char* (*read_rpc_header)(GBinderReader* reader, guint32 txcode,
 | 
			
		||||
        char** iface);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const GBinderRpcProtocol gbinder_rpc_protocol_binder;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -41,9 +41,29 @@
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_idlepool.h>
 | 
			
		||||
#include <gutil_misc.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#define PRESENSE_WAIT_MS_MIN  (100)
 | 
			
		||||
#define PRESENSE_WAIT_MS_MAX  (1000)
 | 
			
		||||
#define PRESENSE_WAIT_MS_STEP (100)
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_watch {
 | 
			
		||||
    char* name;
 | 
			
		||||
    char* detail;
 | 
			
		||||
    GQuark quark;
 | 
			
		||||
    gboolean watched;
 | 
			
		||||
} GBinderServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
struct gbinder_servicemanager_priv {
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
    gulong death_id;
 | 
			
		||||
    gboolean present;
 | 
			
		||||
    guint presence_check_id;
 | 
			
		||||
    guint presence_check_delay_ms;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager,
 | 
			
		||||
    G_TYPE_OBJECT)
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +80,18 @@ G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager,
 | 
			
		||||
#define GBINDER_IS_SERVICEMANAGER_TYPE(klass) \
 | 
			
		||||
    G_TYPE_CHECK_CLASS_TYPE(klass, GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
enum gbinder_servicemanager_signal {
 | 
			
		||||
    SIGNAL_PRESENCE,
 | 
			
		||||
    SIGNAL_REGISTRATION,
 | 
			
		||||
    SIGNAL_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char SIGNAL_PRESENCE_NAME[] = "servicemanager-presence";
 | 
			
		||||
static const char SIGNAL_REGISTRATION_NAME[] = "servicemanager-registration";
 | 
			
		||||
#define DETAIL_LEN 32
 | 
			
		||||
 | 
			
		||||
static guint gbinder_servicemanager_signals[SIGNAL_COUNT] = { 0 };
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -81,53 +113,29 @@ gbinder_servicemanager_class_ref(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_new_with_type(
 | 
			
		||||
    GType type,
 | 
			
		||||
    const char* dev)
 | 
			
		||||
static
 | 
			
		||||
GBinderServiceManagerWatch*
 | 
			
		||||
gbinder_servicemanager_watch_new(
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = NULL;
 | 
			
		||||
    GBinderServiceManagerClass* klass = gbinder_servicemanager_class_ref(type);
 | 
			
		||||
    GBinderServiceManagerWatch* watch = g_new0(GBinderServiceManagerWatch, 1);
 | 
			
		||||
 | 
			
		||||
    if (klass) {
 | 
			
		||||
        GBinderIpc* ipc;
 | 
			
		||||
    watch->name = g_strdup(name);
 | 
			
		||||
    watch->detail = g_compute_checksum_for_string(G_CHECKSUM_MD5, name, -1);
 | 
			
		||||
    watch->quark = g_quark_from_string(watch->detail);
 | 
			
		||||
    return watch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
        if (!dev) dev = klass->default_device;
 | 
			
		||||
        ipc = gbinder_ipc_new(dev, klass->rpc_protocol);
 | 
			
		||||
        if (ipc) {
 | 
			
		||||
            GBinderRemoteObject* object = gbinder_ipc_get_remote_object
 | 
			
		||||
                (ipc, klass->handle);
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_watch_free(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerWatch* watch = data;
 | 
			
		||||
 | 
			
		||||
            if (object) {
 | 
			
		||||
                /* Lock */
 | 
			
		||||
                g_mutex_lock(&klass->mutex);
 | 
			
		||||
                if (klass->table) {
 | 
			
		||||
                    self = g_hash_table_lookup(klass->table, dev);
 | 
			
		||||
                }
 | 
			
		||||
                if (self) {
 | 
			
		||||
                    gbinder_servicemanager_ref(self);
 | 
			
		||||
                } else {
 | 
			
		||||
                    char* key = g_strdup(dev); /* Owned by the hashtable */
 | 
			
		||||
 | 
			
		||||
                    GVERBOSE_("%s", dev);
 | 
			
		||||
                    self = g_object_new(type, NULL);
 | 
			
		||||
                    self->client = gbinder_client_new(object, klass->iface);
 | 
			
		||||
                    self->dev = gbinder_remote_object_dev(object);
 | 
			
		||||
                    if (!klass->table) {
 | 
			
		||||
                        klass->table = g_hash_table_new_full(g_str_hash,
 | 
			
		||||
                            g_str_equal, g_free, NULL);
 | 
			
		||||
                    }
 | 
			
		||||
                    g_hash_table_replace(klass->table, key, self);
 | 
			
		||||
                }
 | 
			
		||||
                g_mutex_unlock(&klass->mutex);
 | 
			
		||||
                /* Unlock */
 | 
			
		||||
                gbinder_remote_object_unref(object);
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_ipc_unref(ipc);
 | 
			
		||||
        }
 | 
			
		||||
        g_type_class_unref(klass);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    g_free(watch->detail);
 | 
			
		||||
    g_free(watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_list_tx_data {
 | 
			
		||||
@@ -157,6 +165,7 @@ gbinder_servicemanager_list_tx_done(
 | 
			
		||||
    if (!data->func(data->sm, data->result, data->user_data)) {
 | 
			
		||||
        g_strfreev(data->result);
 | 
			
		||||
    }
 | 
			
		||||
    data->result = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -166,6 +175,7 @@ gbinder_servicemanager_list_tx_free(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerListTxData* data = user_data;
 | 
			
		||||
 | 
			
		||||
    g_strfreev(data->result);
 | 
			
		||||
    gbinder_servicemanager_unref(data->sm);
 | 
			
		||||
    g_slice_free(GBinderServiceManagerListTxData, data);
 | 
			
		||||
}
 | 
			
		||||
@@ -256,6 +266,230 @@ gbinder_servicemanager_add_service_tx_free(
 | 
			
		||||
    g_slice_free(GBinderServiceManagerAddServiceTxData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_reanimated(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    if (priv->presence_check_id) {
 | 
			
		||||
        g_source_remove(priv->presence_check_id);
 | 
			
		||||
        priv->presence_check_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
    GINFO("Service manager %s has appeared", self->dev);
 | 
			
		||||
    /* Re-arm the watches */
 | 
			
		||||
    if (g_hash_table_size(priv->watch_table) > 0) {
 | 
			
		||||
        gpointer value;
 | 
			
		||||
        GHashTableIter it;
 | 
			
		||||
        GBinderServiceManagerClass* klass =
 | 
			
		||||
            GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
 | 
			
		||||
        g_hash_table_iter_init(&it, priv->watch_table);
 | 
			
		||||
        while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
            GBinderServiceManagerWatch* watch = value;
 | 
			
		||||
 | 
			
		||||
            GASSERT(!watch->watched);
 | 
			
		||||
            watch->watched = klass->watch(self, watch->name);
 | 
			
		||||
            if (watch->watched) {
 | 
			
		||||
                GDEBUG("Watching %s", watch->name);
 | 
			
		||||
            } else {
 | 
			
		||||
                GWARN("Failed to watch %s", watch->name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_PRESENCE], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_presense_check_timer(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = GBINDER_SERVICEMANAGER(user_data);
 | 
			
		||||
    GBinderRemoteObject* remote = self->client->remote;
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
    gboolean result;
 | 
			
		||||
 | 
			
		||||
    GASSERT(remote->dead);
 | 
			
		||||
    gbinder_servicemanager_ref(self);
 | 
			
		||||
    if (gbinder_remote_object_reanimate(remote)) {
 | 
			
		||||
        /* Done */
 | 
			
		||||
        priv->presence_check_id = 0;
 | 
			
		||||
        gbinder_servicemanager_reanimated(self);
 | 
			
		||||
        result = G_SOURCE_REMOVE;
 | 
			
		||||
    } else if (priv->presence_check_delay_ms < PRESENSE_WAIT_MS_MAX) {
 | 
			
		||||
        priv->presence_check_delay_ms += PRESENSE_WAIT_MS_STEP;
 | 
			
		||||
        priv->presence_check_id = g_timeout_add(priv->presence_check_delay_ms,
 | 
			
		||||
            gbinder_servicemanager_presense_check_timer, self);
 | 
			
		||||
        result = G_SOURCE_REMOVE;
 | 
			
		||||
    } else {
 | 
			
		||||
        result = G_SOURCE_CONTINUE;
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_servicemanager_unref(self);
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_presence_check_start(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    GASSERT(!priv->presence_check_id);
 | 
			
		||||
    priv->presence_check_delay_ms = PRESENSE_WAIT_MS_MIN;
 | 
			
		||||
    priv->presence_check_id = g_timeout_add(PRESENSE_WAIT_MS_MIN,
 | 
			
		||||
        gbinder_servicemanager_presense_check_timer, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_died(
 | 
			
		||||
    GBinderRemoteObject* remote,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = GBINDER_SERVICEMANAGER(user_data);
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    GWARN("Service manager %s has died", self->dev);
 | 
			
		||||
    gbinder_servicemanager_presence_check_start(self);
 | 
			
		||||
 | 
			
		||||
    /* Will re-arm watches after servicemanager gets restarted */
 | 
			
		||||
    if (g_hash_table_size(priv->watch_table) > 0) {
 | 
			
		||||
        gpointer value;
 | 
			
		||||
        GHashTableIter it;
 | 
			
		||||
        GBinderServiceManagerClass* klass =
 | 
			
		||||
            GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
 | 
			
		||||
        g_hash_table_iter_init(&it, priv->watch_table);
 | 
			
		||||
        while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
            GBinderServiceManagerWatch* watch = value;
 | 
			
		||||
 | 
			
		||||
            if (watch->watched) {
 | 
			
		||||
                GDEBUG("Unwatching %s", watch->name);
 | 
			
		||||
                watch->watched = FALSE;
 | 
			
		||||
                klass->unwatch(self, watch->name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_PRESENCE], 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_sleep_ms(
 | 
			
		||||
    gulong ms)
 | 
			
		||||
{
 | 
			
		||||
    struct timespec wait;
 | 
			
		||||
 | 
			
		||||
    wait.tv_sec = ms/1000;                /* seconds */
 | 
			
		||||
    wait.tv_nsec = (ms % 1000) * 1000000; /* nanoseconds */
 | 
			
		||||
    while (nanosleep(&wait, &wait) == -1 && errno == EINTR &&
 | 
			
		||||
        (wait.tv_sec > 0 || wait.tv_nsec > 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internal interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_new_with_type(
 | 
			
		||||
    GType type,
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = NULL;
 | 
			
		||||
    GBinderServiceManagerClass* klass = gbinder_servicemanager_class_ref(type);
 | 
			
		||||
 | 
			
		||||
    if (klass) {
 | 
			
		||||
        GBinderIpc* ipc;
 | 
			
		||||
 | 
			
		||||
        if (!dev) dev = klass->default_device;
 | 
			
		||||
        ipc = gbinder_ipc_new(dev, klass->rpc_protocol);
 | 
			
		||||
        if (ipc) {
 | 
			
		||||
            /* Create a possible dead remote object */
 | 
			
		||||
            GBinderRemoteObject* object = gbinder_ipc_get_remote_object
 | 
			
		||||
                (ipc, GBINDER_SERVICEMANAGER_HANDLE, TRUE);
 | 
			
		||||
 | 
			
		||||
            if (object) {
 | 
			
		||||
                gboolean first_ref;
 | 
			
		||||
 | 
			
		||||
                /* Lock */
 | 
			
		||||
                g_mutex_lock(&klass->mutex);
 | 
			
		||||
                if (klass->table) {
 | 
			
		||||
                    self = g_hash_table_lookup(klass->table, dev);
 | 
			
		||||
                }
 | 
			
		||||
                if (self) {
 | 
			
		||||
                    first_ref = FALSE;
 | 
			
		||||
                    gbinder_servicemanager_ref(self);
 | 
			
		||||
                } else {
 | 
			
		||||
                    char* key = g_strdup(dev); /* Owned by the hashtable */
 | 
			
		||||
 | 
			
		||||
                    first_ref = TRUE;
 | 
			
		||||
                    self = g_object_new(type, NULL);
 | 
			
		||||
                    self->client = gbinder_client_new(object, klass->iface);
 | 
			
		||||
                    self->dev = gbinder_remote_object_dev(object);
 | 
			
		||||
                    if (!klass->table) {
 | 
			
		||||
                        klass->table = g_hash_table_new_full(g_str_hash,
 | 
			
		||||
                            g_str_equal, g_free, NULL);
 | 
			
		||||
                    }
 | 
			
		||||
                    g_hash_table_replace(klass->table, key, self);
 | 
			
		||||
                }
 | 
			
		||||
                g_mutex_unlock(&klass->mutex);
 | 
			
		||||
                /* Unlock */
 | 
			
		||||
                if (first_ref) {
 | 
			
		||||
                    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
                    priv->death_id =
 | 
			
		||||
                        gbinder_remote_object_add_death_handler(object,
 | 
			
		||||
                            gbinder_servicemanager_died, self);
 | 
			
		||||
                    /* Query the actual state if necessary */
 | 
			
		||||
                    gbinder_remote_object_reanimate(object);
 | 
			
		||||
                    if (object->dead) {
 | 
			
		||||
                        gbinder_servicemanager_presence_check_start(self);
 | 
			
		||||
                    }
 | 
			
		||||
                    GDEBUG("%s has %sservice manager", dev,
 | 
			
		||||
                        object->dead ? "no " : "");
 | 
			
		||||
                }
 | 
			
		||||
                gbinder_remote_object_unref(object);
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_ipc_unref(ipc);
 | 
			
		||||
        }
 | 
			
		||||
        g_type_class_unref(klass);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_service_registered(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
    GBinderServiceManagerWatch* watch = NULL;
 | 
			
		||||
    const char* normalized_name;
 | 
			
		||||
    char* tmp_name = NULL;
 | 
			
		||||
 | 
			
		||||
    switch (klass->check_name(self, name)) {
 | 
			
		||||
    case GBINDER_SERVICEMANAGER_NAME_OK:
 | 
			
		||||
        normalized_name = name;
 | 
			
		||||
        break;
 | 
			
		||||
    case GBINDER_SERVICEMANAGER_NAME_NORMALIZE:
 | 
			
		||||
        normalized_name = tmp_name = klass->normalize_name(self, name);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        normalized_name = NULL;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (normalized_name) {
 | 
			
		||||
        watch = g_hash_table_lookup(priv->watch_table, normalized_name);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(tmp_name);
 | 
			
		||||
    g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_REGISTRATION],
 | 
			
		||||
        watch ? watch->quark : 0, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -277,10 +511,25 @@ gbinder_servicemanager_new_local_object(
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    const char* ifaces[2];
 | 
			
		||||
 | 
			
		||||
    ifaces[0] = iface;
 | 
			
		||||
    ifaces[1] = NULL;
 | 
			
		||||
    return gbinder_servicemanager_new_local_object2
 | 
			
		||||
        (self, ifaces, txproc, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_servicemanager_new_local_object2(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        return gbinder_ipc_new_local_object(gbinder_client_ipc(self->client),
 | 
			
		||||
            iface, txproc, user_data);
 | 
			
		||||
        return gbinder_local_object_new(gbinder_client_ipc(self->client),
 | 
			
		||||
            ifaces, txproc, user_data);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -304,6 +553,58 @@ gbinder_servicemanager_unref(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_is_present(
 | 
			
		||||
    GBinderServiceManager* self) /* Since 1.0.25 */
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) && !self->client->remote->dead;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicemanager_wait(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    long max_wait_ms) /* Since 1.0.25 */
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* remote = self->client->remote;
 | 
			
		||||
 | 
			
		||||
        if (!remote->dead) {
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else if (gbinder_remote_object_reanimate(remote)) {
 | 
			
		||||
            gbinder_servicemanager_reanimated(self);
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        } else if (max_wait_ms != 0) {
 | 
			
		||||
            /* Zero timeout means a singe check and it's already done */
 | 
			
		||||
            long delay_ms = PRESENSE_WAIT_MS_MIN;
 | 
			
		||||
 | 
			
		||||
            while (max_wait_ms != 0) {
 | 
			
		||||
                if (max_wait_ms > 0) {
 | 
			
		||||
                    if (max_wait_ms < delay_ms) {
 | 
			
		||||
                        delay_ms = max_wait_ms;
 | 
			
		||||
                        max_wait_ms = 0;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        max_wait_ms -= delay_ms;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                gbinder_servicemanager_sleep_ms(delay_ms);
 | 
			
		||||
                if (gbinder_remote_object_reanimate(remote)) {
 | 
			
		||||
                    gbinder_servicemanager_reanimated(self);
 | 
			
		||||
                    return TRUE;
 | 
			
		||||
                }
 | 
			
		||||
                if (delay_ms < PRESENSE_WAIT_MS_MAX) {
 | 
			
		||||
                    delay_ms += PRESENSE_WAIT_MS_STEP;
 | 
			
		||||
                    if (delay_ms > PRESENSE_WAIT_MS_MAX) {
 | 
			
		||||
                        delay_ms = PRESENSE_WAIT_MS_MAX;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            /* Timeout */
 | 
			
		||||
            GWARN("Timeout waiting for service manager %s", self->dev);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
@@ -433,6 +734,118 @@ gbinder_servicemanager_cancel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_add_presence_handler(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    GBinderServiceManagerFunc func,
 | 
			
		||||
    void* user_data) /* Since 1.0.25 */
 | 
			
		||||
{
 | 
			
		||||
    return (G_LIKELY(self) && G_LIKELY(func)) ? g_signal_connect(self,
 | 
			
		||||
        SIGNAL_PRESENCE_NAME, G_CALLBACK(func), user_data) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_add_registration_handler(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderServiceManagerRegistrationFunc func,
 | 
			
		||||
    void* data) /* Since 1.0.13 */
 | 
			
		||||
{
 | 
			
		||||
    gulong id = 0;
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(func)) {
 | 
			
		||||
        char* tmp_name = NULL;
 | 
			
		||||
        GBinderServiceManagerClass* klass =
 | 
			
		||||
            GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
 | 
			
		||||
        switch (klass->check_name(self, name)) {
 | 
			
		||||
        case GBINDER_SERVICEMANAGER_NAME_OK:
 | 
			
		||||
            break;
 | 
			
		||||
        case GBINDER_SERVICEMANAGER_NAME_NORMALIZE:
 | 
			
		||||
            name = tmp_name = klass->normalize_name(self, name);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            name = NULL;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (name) {
 | 
			
		||||
            GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
            GBinderServiceManagerWatch* watch = NULL;
 | 
			
		||||
 | 
			
		||||
            watch = g_hash_table_lookup(priv->watch_table, name);
 | 
			
		||||
            if (!watch) {
 | 
			
		||||
                watch = gbinder_servicemanager_watch_new(name);
 | 
			
		||||
                g_hash_table_insert(priv->watch_table, watch->name, watch);
 | 
			
		||||
            }
 | 
			
		||||
            if (!watch->watched && !self->client->remote->dead) {
 | 
			
		||||
                watch->watched = klass->watch(self, watch->name);
 | 
			
		||||
                if (watch->watched) {
 | 
			
		||||
                    GDEBUG("Watching %s", watch->name);
 | 
			
		||||
                } else {
 | 
			
		||||
                    GWARN("Failed to watch %s", watch->name);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            id = g_signal_connect_closure_by_id(self,
 | 
			
		||||
                gbinder_servicemanager_signals[SIGNAL_REGISTRATION],
 | 
			
		||||
                watch->quark, g_cclosure_new(G_CALLBACK(func), data, NULL),
 | 
			
		||||
                FALSE);
 | 
			
		||||
        }
 | 
			
		||||
        g_free(tmp_name);
 | 
			
		||||
    }
 | 
			
		||||
    return id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_remove_handler(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    gulong id) /* Since 1.0.13 */
 | 
			
		||||
{
 | 
			
		||||
    gbinder_servicemanager_remove_handlers(self, &id, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_remove_handlers(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    gulong* ids,
 | 
			
		||||
    guint count) /* Since 1.0.25 */
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(ids) && G_LIKELY(count)) {
 | 
			
		||||
        guint i, disconnected = 0;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
            if (ids[i]) {
 | 
			
		||||
                g_signal_handler_disconnect(self, ids[i]);
 | 
			
		||||
                disconnected++;
 | 
			
		||||
                ids[i] = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (disconnected) {
 | 
			
		||||
            GBinderServiceManagerClass* klass =
 | 
			
		||||
                GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
            GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
            GHashTableIter it;
 | 
			
		||||
            gpointer value;
 | 
			
		||||
 | 
			
		||||
            g_hash_table_iter_init(&it, priv->watch_table);
 | 
			
		||||
            while (disconnected && g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
                GBinderServiceManagerWatch* watch = value;
 | 
			
		||||
 | 
			
		||||
                if (watch->watched && !g_signal_has_handler_pending(self,
 | 
			
		||||
                    gbinder_servicemanager_signals[SIGNAL_REGISTRATION],
 | 
			
		||||
                    watch->quark, TRUE)) {
 | 
			
		||||
                    /* This must be one of those we have just removed */
 | 
			
		||||
                    GDEBUG("Unwatching %s", watch->name);
 | 
			
		||||
                    watch->watched = FALSE;
 | 
			
		||||
                    klass->unwatch(self, watch->name);
 | 
			
		||||
                    disconnected--;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -442,6 +855,12 @@ void
 | 
			
		||||
gbinder_servicemanager_init(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
 | 
			
		||||
        GBINDER_TYPE_SERVICEMANAGER, GBinderServiceManagerPriv);
 | 
			
		||||
 | 
			
		||||
    self->priv = priv;
 | 
			
		||||
    priv->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
        NULL, gbinder_servicemanager_watch_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -450,8 +869,7 @@ gbinder_servicemanager_dispose(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = GBINDER_SERVICEMANAGER(object);
 | 
			
		||||
    GBinderServiceManagerClass* klass =
 | 
			
		||||
        GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
    GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE_("%s", self->dev);
 | 
			
		||||
    /* Lock */
 | 
			
		||||
@@ -493,9 +911,14 @@ gbinder_servicemanager_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManager* self = GBINDER_SERVICEMANAGER(object);
 | 
			
		||||
    GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
    gutil_idle_pool_drain(self->pool);
 | 
			
		||||
    gutil_idle_pool_unref(self->pool);
 | 
			
		||||
    if (priv->presence_check_id) {
 | 
			
		||||
        g_source_remove(priv->presence_check_id);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_remote_object_remove_handler(self->client->remote, priv->death_id);
 | 
			
		||||
    g_hash_table_destroy(priv->watch_table);
 | 
			
		||||
    gutil_idle_pool_destroy(self->pool);
 | 
			
		||||
    gbinder_client_unref(self->client);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
@@ -506,10 +929,20 @@ gbinder_servicemanager_class_init(
 | 
			
		||||
    GBinderServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object_class = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GType type = G_OBJECT_CLASS_TYPE(klass);
 | 
			
		||||
 | 
			
		||||
    g_mutex_init(&klass->mutex);
 | 
			
		||||
    g_type_class_add_private(klass, sizeof(GBinderServiceManagerPriv));
 | 
			
		||||
    object_class->dispose = gbinder_servicemanager_dispose;
 | 
			
		||||
    object_class->finalize = gbinder_servicemanager_finalize;
 | 
			
		||||
    gbinder_servicemanager_signals[SIGNAL_PRESENCE] =
 | 
			
		||||
        g_signal_new(SIGNAL_PRESENCE_NAME, type,
 | 
			
		||||
            G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL,
 | 
			
		||||
            G_TYPE_NONE, 0);
 | 
			
		||||
    gbinder_servicemanager_signals[SIGNAL_REGISTRATION] =
 | 
			
		||||
        g_signal_new(SIGNAL_REGISTRATION_NAME, type,
 | 
			
		||||
            G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL,
 | 
			
		||||
            G_TYPE_NONE, 1, G_TYPE_STRING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -39,19 +39,30 @@
 | 
			
		||||
 | 
			
		||||
#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 {
 | 
			
		||||
    GObject parent;
 | 
			
		||||
    GBinderServiceManagerPriv* priv;
 | 
			
		||||
    const char* dev;
 | 
			
		||||
    GBinderClient* client;
 | 
			
		||||
    GUtilIdlePool* pool;
 | 
			
		||||
} GBinderServiceManager;
 | 
			
		||||
 | 
			
		||||
typedef enum gbinder_servicemanager_name_check {
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_OK,
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_NORMALIZE,
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_INVALID,
 | 
			
		||||
} GBINDER_SERVICEMANAGER_NAME_CHECK;
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_class {
 | 
			
		||||
    GObjectClass parent;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    GHashTable* table;
 | 
			
		||||
 | 
			
		||||
    guint32 handle;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
    const char* default_device;
 | 
			
		||||
    const GBinderRpcProtocol* rpc_protocol;
 | 
			
		||||
@@ -63,6 +74,15 @@ typedef struct gbinder_servicemanager_class {
 | 
			
		||||
    int (*add_service)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name,
 | 
			
		||||
            GBinderLocalObject* obj);
 | 
			
		||||
 | 
			
		||||
    /* Checking/normalizing watch names */
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name);
 | 
			
		||||
    char* (*normalize_name)(GBinderServiceManager* self, const char* name);
 | 
			
		||||
 | 
			
		||||
    /* If watch() returns FALSE, unwatch() is not called */
 | 
			
		||||
    gboolean (*watch)(GBinderServiceManager* self, const char* name);
 | 
			
		||||
    void (*unwatch)(GBinderServiceManager* self, const char* name);
 | 
			
		||||
} GBinderServiceManagerClass;
 | 
			
		||||
 | 
			
		||||
GType gbinder_servicemanager_get_type(void);
 | 
			
		||||
@@ -73,6 +93,11 @@ gbinder_servicemanager_new_with_type(
 | 
			
		||||
    GType type,
 | 
			
		||||
    const char* dev);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_service_registered(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										173
									
								
								src/gbinder_servicename.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/gbinder_servicename.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019 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_types_p.h"
 | 
			
		||||
#include "gbinder_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
 | 
			
		||||
/* Since 1.0.26 */
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicename_priv {
 | 
			
		||||
    GBinderServiceName pub;
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    char* name;
 | 
			
		||||
    GBinderLocalObject* object;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    gulong presence_id;
 | 
			
		||||
    gulong add_call_id;
 | 
			
		||||
} GBinderServiceNamePriv;
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC GBinderServiceNamePriv*
 | 
			
		||||
gbinder_servicename_cast(GBinderServiceName* pub)
 | 
			
		||||
    { return G_CAST(pub, GBinderServiceNamePriv, pub); }
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_add_service_done(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceNamePriv* priv = user_data;
 | 
			
		||||
 | 
			
		||||
    GASSERT(priv->add_call_id);
 | 
			
		||||
    priv->add_call_id = 0;
 | 
			
		||||
    if (status) {
 | 
			
		||||
        GWARN("Error %d adding name \"%s\"", status, priv->name);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Service \"%s\" has been registered", priv->name);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_add_service(
 | 
			
		||||
    GBinderServiceNamePriv* priv)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("Adding service \"%s\"", priv->name);
 | 
			
		||||
    gbinder_servicemanager_cancel(priv->sm, priv->add_call_id);
 | 
			
		||||
    priv->add_call_id = gbinder_servicemanager_add_service(priv->sm,
 | 
			
		||||
        priv->name, priv->object, gbinder_servicename_add_service_done, priv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_presence_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceNamePriv* priv = user_data;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderServiceName*
 | 
			
		||||
gbinder_servicename_new(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(sm) && G_LIKELY(object) && G_LIKELY(name)) {
 | 
			
		||||
        GBinderServiceNamePriv* priv = g_slice_new0(GBinderServiceNamePriv);
 | 
			
		||||
        GBinderServiceName* self = &priv->pub;
 | 
			
		||||
 | 
			
		||||
        g_atomic_int_set(&priv->refcount, 1);
 | 
			
		||||
        priv->object = gbinder_local_object_ref(object);
 | 
			
		||||
        priv->sm = gbinder_servicemanager_ref(sm);
 | 
			
		||||
        self->name = priv->name = g_strdup(name);
 | 
			
		||||
        priv->presence_id = gbinder_servicemanager_add_presence_handler(sm,
 | 
			
		||||
            gbinder_servicename_presence_handler, priv);
 | 
			
		||||
        if (gbinder_servicemanager_is_present(sm)) {
 | 
			
		||||
            gbinder_servicename_add_service(priv);
 | 
			
		||||
        }
 | 
			
		||||
        return self;
 | 
			
		||||
    } else {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceName*
 | 
			
		||||
gbinder_servicename_ref(
 | 
			
		||||
    GBinderServiceName* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderServiceNamePriv* priv = gbinder_servicename_cast(self);
 | 
			
		||||
 | 
			
		||||
        GASSERT(priv->refcount > 0);
 | 
			
		||||
        g_atomic_int_inc(&priv->refcount);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicename_unref(
 | 
			
		||||
    GBinderServiceName* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderServiceNamePriv* priv = gbinder_servicename_cast(self);
 | 
			
		||||
 | 
			
		||||
        GASSERT(priv->refcount > 0);
 | 
			
		||||
        if (g_atomic_int_dec_and_test(&priv->refcount)) {
 | 
			
		||||
            gbinder_servicemanager_cancel(priv->sm, priv->add_call_id);
 | 
			
		||||
            gbinder_servicemanager_remove_handler(priv->sm, priv->presence_id);
 | 
			
		||||
            gbinder_servicemanager_unref(priv->sm);
 | 
			
		||||
            gbinder_local_object_unref(priv->object);
 | 
			
		||||
            g_free(priv->name);
 | 
			
		||||
            g_slice_free(GBinderServiceName, self);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										268
									
								
								src/gbinder_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								src/gbinder_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_servicemanager.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
/* This is configurable mostly so that unit testing doesn't take too long */
 | 
			
		||||
guint gbinder_servicepoll_interval_ms = 2000;
 | 
			
		||||
 | 
			
		||||
typedef GObjectClass GBinderServicePollClass;
 | 
			
		||||
struct gbinder_servicepoll {
 | 
			
		||||
    GObject object;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    char** list;
 | 
			
		||||
    gulong list_id;
 | 
			
		||||
    guint timer_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderServicePoll, gbinder_servicepoll, G_TYPE_OBJECT)
 | 
			
		||||
#define GBINDER_TYPE_SERVICEPOLL (gbinder_servicepoll_get_type())
 | 
			
		||||
#define GBINDER_SERVICEPOLL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_SERVICEPOLL, GBinderServicePoll))
 | 
			
		||||
 | 
			
		||||
enum gbinder_servicepoll_signal {
 | 
			
		||||
    SIGNAL_NAME_ADDED,
 | 
			
		||||
    SIGNAL_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char SIGNAL_NAME_ADDED_NAME[] = "servicepoll-name-added";
 | 
			
		||||
 | 
			
		||||
static guint gbinder_servicepoll_signals[SIGNAL_COUNT] = { 0 };
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
/* GBinderServiceManagerListFunc callback returns TRUE to keep the services
 | 
			
		||||
 * list, otherwise the caller will deallocate it. */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_list(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicepoll_ref(self);
 | 
			
		||||
    self->list_id = 0;
 | 
			
		||||
    if (services) {
 | 
			
		||||
        const GStrV* ptr_new;
 | 
			
		||||
 | 
			
		||||
        ptr_new = services = gutil_strv_sort(services, TRUE);
 | 
			
		||||
        if (self->list) {
 | 
			
		||||
            const GStrV* ptr_old = self->list;
 | 
			
		||||
 | 
			
		||||
            while (*ptr_new && *ptr_old) {
 | 
			
		||||
                const int i = gutil_strv_find(ptr_old, *ptr_new);
 | 
			
		||||
 | 
			
		||||
                if (i < 0) {
 | 
			
		||||
                    /* New name */
 | 
			
		||||
                    g_signal_emit(self, gbinder_servicepoll_signals
 | 
			
		||||
                        [SIGNAL_NAME_ADDED], 0, *ptr_new);
 | 
			
		||||
                } else {
 | 
			
		||||
                    int k;
 | 
			
		||||
 | 
			
		||||
                    /* If some names have disappeared, then i may be > 0 */
 | 
			
		||||
                    for (k = 0; k < i; k ++) ptr_old++;
 | 
			
		||||
                    ptr_old++;
 | 
			
		||||
                }
 | 
			
		||||
                ptr_new++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        while (*ptr_new) {
 | 
			
		||||
            g_signal_emit(self, gbinder_servicepoll_signals
 | 
			
		||||
                [SIGNAL_NAME_ADDED], 0, *ptr_new);
 | 
			
		||||
            ptr_new++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_strfreev(self->list);
 | 
			
		||||
    self->list = services;
 | 
			
		||||
    gbinder_servicepoll_unref(self);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_timer(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
 | 
			
		||||
 | 
			
		||||
    if (!self->list_id) {
 | 
			
		||||
        self->list_id = gbinder_servicemanager_list(self->manager,
 | 
			
		||||
            gbinder_servicepoll_list, self);
 | 
			
		||||
    }
 | 
			
		||||
    return G_SOURCE_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_create(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = g_object_new(GBINDER_TYPE_SERVICEPOLL, NULL);
 | 
			
		||||
 | 
			
		||||
    self->manager = gbinder_servicemanager_ref(manager);
 | 
			
		||||
    self->list_id = gbinder_servicemanager_list(manager,
 | 
			
		||||
        gbinder_servicepoll_list, self);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * API
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_new(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    GBinderServicePoll** weakptr)
 | 
			
		||||
{
 | 
			
		||||
    if (weakptr) {
 | 
			
		||||
        if (*weakptr) {
 | 
			
		||||
            gbinder_servicepoll_ref(*weakptr);
 | 
			
		||||
        } else {
 | 
			
		||||
            *weakptr = gbinder_servicepoll_create(manager);
 | 
			
		||||
            g_object_add_weak_pointer(G_OBJECT(*weakptr), (gpointer*)weakptr);
 | 
			
		||||
        }
 | 
			
		||||
        return *weakptr;
 | 
			
		||||
    } else {
 | 
			
		||||
        return gbinder_servicepoll_create(manager);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_ref(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_ref(GBINDER_SERVICEPOLL(self));
 | 
			
		||||
        return self;
 | 
			
		||||
    } else {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_unref(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_unref(GBINDER_SERVICEPOLL(self));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicepoll_manager(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->manager : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_is_known_name(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) && gutil_strv_contains(self->list, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicepoll_add_handler(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    GBinderServicePollFunc fn,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
 | 
			
		||||
        SIGNAL_NAME_ADDED_NAME, G_CALLBACK(fn), user_data) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_remove_handler(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    gulong id)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(id)) {
 | 
			
		||||
        g_signal_handler_disconnect(self, id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_init(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    self->timer_id = g_timeout_add(gbinder_servicepoll_interval_ms,
 | 
			
		||||
        gbinder_servicepoll_timer, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(object);
 | 
			
		||||
 | 
			
		||||
    g_source_remove(self->timer_id);
 | 
			
		||||
    gbinder_servicemanager_cancel(self->manager, self->list_id);
 | 
			
		||||
    gbinder_servicemanager_unref(self->manager);
 | 
			
		||||
    g_strfreev(self->list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_class_init(
 | 
			
		||||
    GBinderServicePollClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_servicepoll_finalize;
 | 
			
		||||
    gbinder_servicepoll_signals[SIGNAL_NAME_ADDED] =
 | 
			
		||||
        g_signal_new(SIGNAL_NAME_ADDED_NAME, G_OBJECT_CLASS_TYPE(klass),
 | 
			
		||||
            G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
 | 
			
		||||
            1, G_TYPE_STRING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										88
									
								
								src/gbinder_servicepoll.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/gbinder_servicepoll.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_SERVICEPOLL_H
 | 
			
		||||
#define GBINDER_SERVICEPOLL_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
extern guint gbinder_servicepoll_interval_ms;
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*GBinderServicePollFunc)(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_new(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    GBinderServicePoll** weakptr);
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_ref(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_unref(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicepoll_manager(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_is_known_name(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicepoll_add_handler(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    GBinderServicePollFunc func,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_remove_handler(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    gulong id);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEPOLL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,37 +35,16 @@
 | 
			
		||||
 | 
			
		||||
#include <gbinder_types.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_buffer_memory GBinderBufferMemory;
 | 
			
		||||
typedef struct gbinder_buffer_contents GBinderBufferContents;
 | 
			
		||||
typedef struct gbinder_cleanup GBinderCleanup;
 | 
			
		||||
typedef struct gbinder_driver GBinderDriver;
 | 
			
		||||
typedef struct gbinder_handler GBinderHandler;
 | 
			
		||||
typedef struct gbinder_io GBinderIo;
 | 
			
		||||
typedef struct gbinder_ipc GBinderIpc;
 | 
			
		||||
typedef struct gbinder_object_registry GBinderObjectRegistry;
 | 
			
		||||
typedef struct gbinder_output_data GBinderOutputData;
 | 
			
		||||
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
 | 
			
		||||
 | 
			
		||||
typedef struct hidl_vec {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const void* ptr;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 count;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
} HidlVec;
 | 
			
		||||
 | 
			
		||||
#define HIDL_VEC_BUFFER_OFFSET (0)
 | 
			
		||||
 | 
			
		||||
typedef struct hidl_string {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const char* str;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 len;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
} HidlString;
 | 
			
		||||
 | 
			
		||||
#define HIDL_STRING_BUFFER_OFFSET (0)
 | 
			
		||||
typedef struct gbinder_servicepoll GBinderServicePoll;
 | 
			
		||||
typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_INLINE_FUNC static inline
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -39,7 +39,10 @@
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_writer_priv {
 | 
			
		||||
    GBinderWriterData* data;
 | 
			
		||||
@@ -54,47 +57,49 @@ GBINDER_INLINE_FUNC GBinderWriterData* gbinder_writer_data(GBinderWriter* pub)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_memory_cleanup(
 | 
			
		||||
    gpointer memory)
 | 
			
		||||
gbinder_writer_data_buffer_cleanup(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_buffer_memory_unref(memory);
 | 
			
		||||
    gbinder_buffer_contents_unref((GBinderBufferContents*)data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_set_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects)
 | 
			
		||||
    GBinderBuffer* buffer)
 | 
			
		||||
{
 | 
			
		||||
    gsize bufsize;
 | 
			
		||||
    const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize);
 | 
			
		||||
    const GBinderIo* io = gbinder_buffer_io(buffer);
 | 
			
		||||
    GBinderBufferMemory* mem = gbinder_buffer_memory(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);
 | 
			
		||||
 | 
			
		||||
    g_byte_array_append(data->bytes, bufdata, bufsize);
 | 
			
		||||
    if (mem) {
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup,
 | 
			
		||||
            gbinder_writer_data_memory_cleanup,
 | 
			
		||||
            gbinder_buffer_memory_ref(mem));
 | 
			
		||||
    }
 | 
			
		||||
    if (objects && *objects) {
 | 
			
		||||
        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);
 | 
			
		||||
    if (contents) {
 | 
			
		||||
        void** objects = gbinder_buffer_objects(buffer);
 | 
			
		||||
 | 
			
		||||
            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->cleanup = gbinder_cleanup_add(data->cleanup,
 | 
			
		||||
            gbinder_writer_data_buffer_cleanup,
 | 
			
		||||
            gbinder_buffer_contents_ref(contents));
 | 
			
		||||
        if (objects && *objects) {
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
                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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -119,16 +124,16 @@ gbinder_writer_data_write_buffer_object(
 | 
			
		||||
    gsize size,
 | 
			
		||||
    const GBinderParent* parent)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* dest = data->bytes;
 | 
			
		||||
    const guint offset = dest->len;
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    guint n;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BUFFER_OBJECT_SIZE);
 | 
			
		||||
    /* Write the object */
 | 
			
		||||
    n = data->io->encode_buffer_object(dest->data + offset, ptr, size, parent);
 | 
			
		||||
    n = data->io->encode_buffer_object(buf->data + offset, ptr, size, parent);
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + n);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + n);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
    /* The driver seems to require each buffer to be 8-byte aligned */
 | 
			
		||||
@@ -144,6 +149,14 @@ gbinder_writer_init(
 | 
			
		||||
    gbinder_writer_cast(self)->data = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gsize
 | 
			
		||||
gbinder_writer_bytes_written(
 | 
			
		||||
    GBinderWriter* self) /* since 1.0.21 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
    return data->bytes->len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_bool(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
@@ -165,7 +178,7 @@ gbinder_writer_data_append_bool(
 | 
			
		||||
 | 
			
		||||
    /* Boolean values are padded to 4-byte boundary */
 | 
			
		||||
    padded[0] = (value != FALSE);
 | 
			
		||||
    padded[1] = padded[2] = padded[3] = 0xff;
 | 
			
		||||
    padded[1] = padded[2] = padded[3] = 0;
 | 
			
		||||
    g_byte_array_append(data->bytes, padded, sizeof(padded));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -186,13 +199,34 @@ gbinder_writer_data_append_int32(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    guint32 value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    guint32* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_overwrite_int32(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    gsize offset,
 | 
			
		||||
    gint32 value) /* since 1.0.21 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
 | 
			
		||||
        if (buf->len >= offset + sizeof(gint32)) {
 | 
			
		||||
            *((gint32*)(buf->data + offset)) = value;
 | 
			
		||||
        } else {
 | 
			
		||||
            GWARN("Can't overwrite at %lu as buffer is only %u bytes long",
 | 
			
		||||
                (gulong)offset, buf->len);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_int64(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
@@ -210,10 +244,11 @@ gbinder_writer_data_append_int64(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    guint64 value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    guint64* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -234,10 +269,11 @@ gbinder_writer_data_append_float(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    gfloat value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    gfloat* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -258,10 +294,11 @@ gbinder_writer_data_append_double(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    gdouble value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    gdouble* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -301,15 +338,16 @@ gbinder_writer_data_append_string8_len(
 | 
			
		||||
    gsize len)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(str)) {
 | 
			
		||||
        const gsize old_size = data->bytes->len;
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        gsize padded_len = G_ALIGN4(len + 1);
 | 
			
		||||
        guint32* dest;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + padded_len);
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len);
 | 
			
		||||
 | 
			
		||||
        /* Zero the last word */
 | 
			
		||||
        dest = (guint32*)(data->bytes->data + old_size);
 | 
			
		||||
        dest = (guint32*)(buf->data + old_size);
 | 
			
		||||
        dest[padded_len/4 - 1] = 0;
 | 
			
		||||
 | 
			
		||||
        /* Copy the data */
 | 
			
		||||
@@ -346,14 +384,36 @@ gbinder_writer_data_append_string16(
 | 
			
		||||
    gbinder_writer_data_append_string16_len(data, utf8, utf8? strlen(utf8) : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_null(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    /* NULL string */
 | 
			
		||||
    gbinder_writer_data_append_int32(data, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_empty(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const gsize old_size = buf->len;
 | 
			
		||||
    guint16* ptr16;
 | 
			
		||||
 | 
			
		||||
    /* Empty string */
 | 
			
		||||
    g_byte_array_set_size(buf, old_size + 8);
 | 
			
		||||
    ptr16 = (guint16*)(buf->data + old_size);
 | 
			
		||||
    ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_len(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const char* utf8,
 | 
			
		||||
    gssize num_bytes)
 | 
			
		||||
{
 | 
			
		||||
    const gsize old_size = data->bytes->len;
 | 
			
		||||
 | 
			
		||||
    if (utf8) {
 | 
			
		||||
        const char* end = utf8;
 | 
			
		||||
 | 
			
		||||
@@ -364,14 +424,16 @@ gbinder_writer_data_append_string16_len(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (num_bytes > 0) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        glong len = g_utf8_strlen(utf8, num_bytes);
 | 
			
		||||
        gsize padded_len = G_ALIGN4((len+1)*2);
 | 
			
		||||
        guint32* len_ptr;
 | 
			
		||||
        gunichar2* utf16_ptr;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + padded_len + 4);
 | 
			
		||||
        len_ptr = (guint32*)(data->bytes->data + old_size);
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len + 4);
 | 
			
		||||
        len_ptr = (guint32*)(buf->data + old_size);
 | 
			
		||||
        utf16_ptr = (gunichar2*)(len_ptr + 1);
 | 
			
		||||
 | 
			
		||||
        /* TODO: this could be optimized for ASCII strings, i.e. if
 | 
			
		||||
@@ -398,17 +460,72 @@ gbinder_writer_data_append_string16_len(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Correct the packet size if necessaary */
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + padded_len + 4);
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len + 4);
 | 
			
		||||
    } else if (utf8) {
 | 
			
		||||
        /* Empty string */
 | 
			
		||||
        guint16* ptr16;
 | 
			
		||||
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + 8);
 | 
			
		||||
        ptr16 = (guint16*)(data->bytes->data + old_size);
 | 
			
		||||
        ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff;
 | 
			
		||||
        gbinder_writer_data_append_string16_empty(data);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* NULL string */
 | 
			
		||||
        gbinder_writer_data_append_int32(data, -1);
 | 
			
		||||
        gbinder_writer_data_append_string16_null(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_utf16(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length)
 | 
			
		||||
{
 | 
			
		||||
    if (length < 0) {
 | 
			
		||||
        length = 0;
 | 
			
		||||
        if (utf16) {
 | 
			
		||||
            const guint16* ptr;
 | 
			
		||||
 | 
			
		||||
            /* Assume NULL terminated string */
 | 
			
		||||
            for (ptr = utf16; *ptr; ptr++);
 | 
			
		||||
            length = ptr - utf16;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (length > 0) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        const gsize padded_size = G_ALIGN4((length + 1) * 2);
 | 
			
		||||
        guint32* len_ptr;
 | 
			
		||||
        gunichar2* utf16_ptr;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_size + 4);
 | 
			
		||||
        len_ptr = (guint32*)(buf->data + old_size);
 | 
			
		||||
        utf16_ptr = (gunichar2*)(len_ptr + 1);
 | 
			
		||||
 | 
			
		||||
        /* Actual length */
 | 
			
		||||
        *len_ptr = length;
 | 
			
		||||
 | 
			
		||||
        /* Characters */
 | 
			
		||||
        memcpy(utf16_ptr, utf16, 2 * length);
 | 
			
		||||
 | 
			
		||||
        /* Zero padding */
 | 
			
		||||
        memset(utf16_ptr + length, 0, padded_size - 2 * length);
 | 
			
		||||
    } else if (utf16) {
 | 
			
		||||
        /* Empty string */
 | 
			
		||||
        gbinder_writer_data_append_string16_empty(data);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* NULL string */
 | 
			
		||||
        gbinder_writer_data_append_string16_null(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string16_utf16(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length) /* Since 1.0.17 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        gbinder_writer_data_append_string16_utf16(data, utf16, length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -436,6 +553,60 @@ gbinder_writer_data_prepare(
 | 
			
		||||
    return data->offsets->count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_close_fd(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    const int fd = GPOINTER_TO_INT(data);
 | 
			
		||||
 | 
			
		||||
    if (close(fd) < 0) {
 | 
			
		||||
        GWARN("Error closing fd %d: %s", fd, strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_fd(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    /* Duplicate the descriptor so that caller can do whatever with
 | 
			
		||||
     * the one it passed in. */
 | 
			
		||||
    const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
 | 
			
		||||
    guint written;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    /* Write the original fd if we failed to dup it */
 | 
			
		||||
    if (dupfd < 0) {
 | 
			
		||||
        GWARN("Error dupping fd %d: %s", fd, strerror(errno));
 | 
			
		||||
        written = data->io->encode_fd_object(buf->data + offset, fd);
 | 
			
		||||
    } else {
 | 
			
		||||
        written = data->io->encode_fd_object(buf->data + offset, dupfd);
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup,
 | 
			
		||||
            gbinder_writer_data_close_fd, GINT_TO_POINTER(dupfd));
 | 
			
		||||
    }
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(buf, offset + written);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_fd(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    int fd) /* Since 1.0.18 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        gbinder_writer_data_append_fd(data, fd);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
gbinder_writer_append_buffer_object_with_parent(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
@@ -498,13 +669,13 @@ gbinder_writer_data_append_hidl_vec(
 | 
			
		||||
    guint elemsize)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent vec_parent;
 | 
			
		||||
    HidlVec* vec = g_new0(HidlVec, 1);
 | 
			
		||||
    GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
 | 
			
		||||
    const gsize total = count * elemsize;
 | 
			
		||||
    void* buf = g_memdup(base, total);
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the string data */
 | 
			
		||||
    vec_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    vec_parent.offset = HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
    vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the vector descriptor */
 | 
			
		||||
    if (buf) {
 | 
			
		||||
@@ -544,12 +715,12 @@ gbinder_writer_data_append_hidl_string(
 | 
			
		||||
    const char* str)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent str_parent;
 | 
			
		||||
    HidlString* hidl_string = g_new0(HidlString, 1);
 | 
			
		||||
    GBinderHidlString* hidl_string = g_new0(GBinderHidlString, 1);
 | 
			
		||||
    const gsize len = str ? strlen(str) : 0;
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the string data */
 | 
			
		||||
    str_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    str_parent.offset = HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
    str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the string descriptor and store it */
 | 
			
		||||
    hidl_string->data.str = str;
 | 
			
		||||
@@ -591,8 +762,8 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
    gssize count)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent vec_parent;
 | 
			
		||||
    HidlVec* vec = g_new0(HidlVec, 1);
 | 
			
		||||
    HidlString* strings = NULL;
 | 
			
		||||
    GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
 | 
			
		||||
    GBinderHidlString* strings = NULL;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (count < 0) {
 | 
			
		||||
@@ -602,11 +773,11 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the vector data */
 | 
			
		||||
    vec_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    vec_parent.offset = HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
    vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the vector descriptor */
 | 
			
		||||
    if (count > 0) {
 | 
			
		||||
        strings = g_new0(HidlString, count);
 | 
			
		||||
        strings = g_new0(GBinderHidlString, count);
 | 
			
		||||
        vec->data.ptr = strings;
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, strings);
 | 
			
		||||
    }
 | 
			
		||||
@@ -617,7 +788,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
    /* Fill in string descriptors */
 | 
			
		||||
    for (i = 0; i < count; i++) {
 | 
			
		||||
        const char* str = strv[i];
 | 
			
		||||
        HidlString* hidl_str = strings + i;
 | 
			
		||||
        GBinderHidlString* hidl_str = strings + i;
 | 
			
		||||
 | 
			
		||||
        if ((hidl_str->data.str = str) != NULL) {
 | 
			
		||||
            hidl_str->len = strlen(str);
 | 
			
		||||
@@ -632,7 +803,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
        /* Prepare parent descriptor for the string data */
 | 
			
		||||
        str_parent.index = data->offsets->count;
 | 
			
		||||
        str_parent.offset = HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
        str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
        /* Write the vector data (it's parent for the string data) */
 | 
			
		||||
        gbinder_writer_data_write_buffer_object(data, strings,
 | 
			
		||||
@@ -640,7 +811,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
        /* Write the string data */
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
            HidlString* hidl_str = strings + i;
 | 
			
		||||
            GBinderHidlString* hidl_str = strings + i;
 | 
			
		||||
 | 
			
		||||
            if (hidl_str->data.str) {
 | 
			
		||||
                gbinder_writer_data_write_buffer_object(data,
 | 
			
		||||
@@ -649,7 +820,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
                    (guint)hidl_str->len, (guint)str_parent.index,
 | 
			
		||||
                    (guint)data->buffers_size);
 | 
			
		||||
            }
 | 
			
		||||
            str_parent.offset += sizeof(HidlString);
 | 
			
		||||
            str_parent.offset += sizeof(GBinderHidlString);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -671,16 +842,16 @@ gbinder_writer_data_append_local_object(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* dest = data->bytes;
 | 
			
		||||
    const guint offset = dest->len;
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    guint n;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    /* Write the object */
 | 
			
		||||
    n = data->io->encode_local_object(dest->data + offset, obj);
 | 
			
		||||
    n = data->io->encode_local_object(buf->data + offset, obj);
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + n);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + n);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
}
 | 
			
		||||
@@ -697,25 +868,122 @@ gbinder_writer_append_remote_object(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_byte_array(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const void* byte_array,
 | 
			
		||||
    gint32 len) /* since 1.0.12 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    GASSERT(len >= 0);
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        void* ptr;
 | 
			
		||||
 | 
			
		||||
        if (!byte_array) {
 | 
			
		||||
            len = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g_byte_array_set_size(buf, buf->len + sizeof(len) + len);
 | 
			
		||||
        ptr = buf->data + (buf->len - sizeof(len) - len);
 | 
			
		||||
 | 
			
		||||
        if (len > 0) {
 | 
			
		||||
            *((gint32*)ptr) = len;
 | 
			
		||||
            ptr += sizeof(len);
 | 
			
		||||
            memcpy(ptr, byte_array, len);
 | 
			
		||||
        } else {
 | 
			
		||||
            *((gint32*)ptr) = -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_remote_object(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* dest = data->bytes;
 | 
			
		||||
    const guint offset = dest->len;
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    guint n;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    /* Write the object */
 | 
			
		||||
    n = data->io->encode_remote_object(dest->data + offset, obj);
 | 
			
		||||
    n = data->io->encode_remote_object(buf->data + offset, obj);
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + n);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + n);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_alloc(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    gsize size,
 | 
			
		||||
    gpointer (*alloc)(gsize),
 | 
			
		||||
    void (*dealloc)())
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        void* ptr = alloc(size);
 | 
			
		||||
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup, dealloc, ptr);
 | 
			
		||||
        return ptr;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_malloc(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    gsize size) /* since 1.0.19 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_writer_alloc(self, size, g_malloc, g_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_malloc0(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    gsize size) /* since 1.0.19 */
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_writer_alloc(self, size, g_malloc0, g_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void*
 | 
			
		||||
gbinder_writer_memdup(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const void* buf,
 | 
			
		||||
    gsize size) /* since 1.0.19 */
 | 
			
		||||
{
 | 
			
		||||
    if (buf) {
 | 
			
		||||
        void* ptr = gbinder_writer_malloc(self, size);
 | 
			
		||||
 | 
			
		||||
        if (ptr) {
 | 
			
		||||
            memcpy(ptr, buf, size);
 | 
			
		||||
            return ptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_add_cleanup(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    GDestroyNotify destroy,
 | 
			
		||||
    gpointer ptr) /* since 1.0.19 */
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(destroy)) {
 | 
			
		||||
        GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(data)) {
 | 
			
		||||
            data->cleanup = gbinder_cleanup_add(data->cleanup, destroy, ptr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 
 | 
			
		||||
@@ -53,8 +53,7 @@ gbinder_writer_init(
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_set_contents(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderBuffer* buffer,
 | 
			
		||||
    void** objects);
 | 
			
		||||
    GBinderBuffer* buffer);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_bool(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
all:
 | 
			
		||||
%:
 | 
			
		||||
	@$(MAKE) -C binder-client $*
 | 
			
		||||
	@$(MAKE) -C binder-dump $*
 | 
			
		||||
	@$(MAKE) -C binder-list $*
 | 
			
		||||
	@$(MAKE) -C binder-ping $*
 | 
			
		||||
	@$(MAKE) -C binder-service $*
 | 
			
		||||
	@$(MAKE) -C rild-card-status $*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018 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
 | 
			
		||||
@@ -54,10 +54,15 @@ typedef struct app_options {
 | 
			
		||||
 | 
			
		||||
typedef struct app {
 | 
			
		||||
    const AppOptions* opt;
 | 
			
		||||
    char* fqname;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderLocalObject* local;
 | 
			
		||||
    GBinderRemoteObject* remote;
 | 
			
		||||
    gulong wait_id;
 | 
			
		||||
    gulong death_id;
 | 
			
		||||
    GBinderClient* client;
 | 
			
		||||
    GThread* thread;
 | 
			
		||||
    int ret;
 | 
			
		||||
} App;
 | 
			
		||||
 | 
			
		||||
@@ -171,46 +176,80 @@ app_input_thread(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_connect_remote(
 | 
			
		||||
    App* app)
 | 
			
		||||
{
 | 
			
		||||
    app->remote = gbinder_servicemanager_get_service_sync(app->sm,
 | 
			
		||||
        app->fqname, NULL); /* autoreleased pointer */
 | 
			
		||||
 | 
			
		||||
    if (app->remote) {
 | 
			
		||||
        const AppOptions* opt = app->opt;
 | 
			
		||||
 | 
			
		||||
        GINFO("Connected to %s", app->fqname);
 | 
			
		||||
        gbinder_remote_object_ref(app->remote);
 | 
			
		||||
        app->client = gbinder_client_new(app->remote, opt->iface);
 | 
			
		||||
        app->death_id = gbinder_remote_object_add_death_handler(app->remote,
 | 
			
		||||
            app_remote_died, app);
 | 
			
		||||
        app->thread = g_thread_new("input", app_input_thread, app);
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_registration_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    App* app = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("\"%s\" appeared", name);
 | 
			
		||||
    if (!strcmp(name, app->fqname) && app_connect_remote(app)) {
 | 
			
		||||
        gbinder_servicemanager_remove_handler(app->sm, app->wait_id);
 | 
			
		||||
        app->wait_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_run(
 | 
			
		||||
   App* app)
 | 
			
		||||
{
 | 
			
		||||
    const AppOptions* opt = app->opt;
 | 
			
		||||
    char* fqname = opt->fqname ? g_strdup(opt->fqname) :
 | 
			
		||||
    guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app);
 | 
			
		||||
    guint sigint = g_unix_signal_add(SIGINT, app_signal, app);
 | 
			
		||||
 | 
			
		||||
    app->fqname = opt->fqname ? g_strdup(opt->fqname) :
 | 
			
		||||
        strchr(opt->name, '/') ? g_strdup(opt->name) :
 | 
			
		||||
        g_strconcat(opt->iface, "/", opt->name, NULL);
 | 
			
		||||
    int status = 0;
 | 
			
		||||
    GBinderRemoteObject* remote = gbinder_remote_object_ref
 | 
			
		||||
        (gbinder_servicemanager_get_service_sync(app->sm, fqname, &status));
 | 
			
		||||
    if (remote) {
 | 
			
		||||
        guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app);
 | 
			
		||||
        guint sigint = g_unix_signal_add(SIGINT, app_signal, app);
 | 
			
		||||
        gulong death_id = gbinder_remote_object_add_death_handler
 | 
			
		||||
            (remote, app_remote_died, app);
 | 
			
		||||
        GThread* thread = g_thread_new("input", app_input_thread, app);
 | 
			
		||||
 | 
			
		||||
        GINFO("Connected to %s\n", fqname);
 | 
			
		||||
 | 
			
		||||
        app->client = gbinder_client_new(remote, opt->iface);
 | 
			
		||||
        app->ret = RET_OK;
 | 
			
		||||
        app->loop = g_main_loop_new(NULL, TRUE);
 | 
			
		||||
        g_main_loop_run(app->loop);
 | 
			
		||||
 | 
			
		||||
        g_source_remove(sigtrm);
 | 
			
		||||
        g_source_remove(sigint);
 | 
			
		||||
        g_main_loop_unref(app->loop);
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_object_remove_handler(remote, death_id);
 | 
			
		||||
        gbinder_remote_object_unref(remote);
 | 
			
		||||
 | 
			
		||||
        /* Not the cleanest exit, just dropping the thread... */
 | 
			
		||||
        g_thread_unref(thread);
 | 
			
		||||
        app->loop = NULL;
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("No such service: %s (%d)", fqname, status);
 | 
			
		||||
    if (!app_connect_remote(app)) {
 | 
			
		||||
        GINFO("Waiting for %s", app->fqname);
 | 
			
		||||
        app->wait_id = gbinder_servicemanager_add_registration_handler(app->sm,
 | 
			
		||||
            app->fqname, app_registration_handler, app);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(fqname);
 | 
			
		||||
 | 
			
		||||
    app->loop = g_main_loop_new(NULL, TRUE);
 | 
			
		||||
    app->ret = RET_OK;
 | 
			
		||||
    g_main_loop_run(app->loop);
 | 
			
		||||
 | 
			
		||||
    g_source_remove(sigtrm);
 | 
			
		||||
    g_source_remove(sigint);
 | 
			
		||||
    g_main_loop_unref(app->loop);
 | 
			
		||||
 | 
			
		||||
    if (app->thread) {
 | 
			
		||||
        /* Not the cleanest of exits, just dropping the thread... */
 | 
			
		||||
        g_thread_unref(app->thread);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_remote_object_remove_handler(app->remote, app->death_id);
 | 
			
		||||
    gbinder_remote_object_unref(app->remote);
 | 
			
		||||
    gbinder_local_object_drop(app->local);
 | 
			
		||||
    gbinder_client_unref(app->client);
 | 
			
		||||
    g_free(app->fqname);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -311,8 +350,6 @@ int main(int argc, char* argv[])
 | 
			
		||||
            app.local = gbinder_servicemanager_new_local_object(app.sm,
 | 
			
		||||
                NULL, NULL, NULL);
 | 
			
		||||
            app_run(&app);
 | 
			
		||||
            gbinder_local_object_unref(app.local);
 | 
			
		||||
            gbinder_client_unref(app.client);
 | 
			
		||||
            gbinder_servicemanager_unref(app.sm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								test/binder-dump/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								test/binder-dump/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
.PHONY: all debug release clean cleaner
 | 
			
		||||
.PHONY: libgbinder-release libgbinder-debug
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Required packages
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Default target
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
all: debug release
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Executable
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
EXE = binder-dump
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# 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)
 | 
			
		||||
							
								
								
									
										237
									
								
								test/binder-dump/binder-dump.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								test/binder-dump/binder-dump.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gbinder.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#define RET_OK          (0)
 | 
			
		||||
#define RET_NOTFOUND    (1)
 | 
			
		||||
#define RET_INVARG      (2)
 | 
			
		||||
#define RET_ERR         (3)
 | 
			
		||||
 | 
			
		||||
#define DEV_DEFAULT     GBINDER_DEFAULT_BINDER
 | 
			
		||||
 | 
			
		||||
#define GBINDER_TRANSACTION(c2,c3,c4)     GBINDER_FOURCC('_',c2,c3,c4)
 | 
			
		||||
#define GBINDER_DUMP_TRANSACTION          GBINDER_TRANSACTION('D','M','P')
 | 
			
		||||
 | 
			
		||||
typedef struct app_options {
 | 
			
		||||
    char* dev;
 | 
			
		||||
    const char* service;
 | 
			
		||||
} AppOptions;
 | 
			
		||||
 | 
			
		||||
typedef struct app {
 | 
			
		||||
    const AppOptions* opt;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    int ret;
 | 
			
		||||
} App;
 | 
			
		||||
 | 
			
		||||
static const char pname[] = "binder-dump";
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_dump_service(
 | 
			
		||||
    App* app,
 | 
			
		||||
    const char* service)
 | 
			
		||||
{
 | 
			
		||||
    int status = 0;
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_servicemanager_get_service_sync(app->sm,
 | 
			
		||||
        service, &status);
 | 
			
		||||
 | 
			
		||||
    if (obj) {
 | 
			
		||||
        GBinderClient* client = gbinder_client_new(obj, NULL);
 | 
			
		||||
        GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
        GBinderRemoteReply* reply;
 | 
			
		||||
        GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_object_ref(obj);
 | 
			
		||||
        gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
        gbinder_writer_append_fd(&writer, STDOUT_FILENO);
 | 
			
		||||
        gbinder_writer_append_int32(&writer, 0);
 | 
			
		||||
        reply = gbinder_client_transact_sync_reply(client,
 | 
			
		||||
            GBINDER_DUMP_TRANSACTION, req, &status);
 | 
			
		||||
        if (status < 0) {
 | 
			
		||||
            GERR("Error %d", status);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_remote_object_unref(obj);
 | 
			
		||||
        gbinder_remote_reply_unref(reply);
 | 
			
		||||
        gbinder_local_request_unref(req);
 | 
			
		||||
        gbinder_client_unref(client);
 | 
			
		||||
        return TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("No such service: %s (%d)", service, status);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_dump_services(
 | 
			
		||||
    App* app,
 | 
			
		||||
    char** strv)
 | 
			
		||||
{
 | 
			
		||||
    if (strv) {
 | 
			
		||||
        while (*strv) {
 | 
			
		||||
            const char* name = *strv++;
 | 
			
		||||
 | 
			
		||||
            printf("========= %s\n", name);
 | 
			
		||||
            app_dump_service(app, name);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_run(
 | 
			
		||||
    App* app)
 | 
			
		||||
{
 | 
			
		||||
    const AppOptions* opt = app->opt;
 | 
			
		||||
 | 
			
		||||
    if (opt->service) {
 | 
			
		||||
        app->ret = app_dump_service(app, opt->service) ? RET_OK : RET_NOTFOUND;
 | 
			
		||||
    } else {
 | 
			
		||||
        char** services = gbinder_servicemanager_list_sync(app->sm);
 | 
			
		||||
 | 
			
		||||
        if (services) {
 | 
			
		||||
            app_dump_services(app, services);
 | 
			
		||||
            g_strfreev(services);
 | 
			
		||||
            app->ret = RET_OK;
 | 
			
		||||
        } else {
 | 
			
		||||
            app->ret = RET_ERR;
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_ERR;
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_init(
 | 
			
		||||
    AppOptions* opt,
 | 
			
		||||
    int argc,
 | 
			
		||||
    char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    gboolean ok = FALSE;
 | 
			
		||||
    GOptionEntry entries[] = {
 | 
			
		||||
        { "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 | 
			
		||||
          app_log_verbose, "Enable verbose output", NULL },
 | 
			
		||||
        { "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 | 
			
		||||
          app_log_quiet, "Be quiet", NULL },
 | 
			
		||||
        { NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GError* error = NULL;
 | 
			
		||||
    GOptionContext* options = g_option_context_new("[SERVICE]");
 | 
			
		||||
 | 
			
		||||
    memset(opt, 0, sizeof(*opt));
 | 
			
		||||
 | 
			
		||||
    gutil_log_timestamp = FALSE;
 | 
			
		||||
    gutil_log_set_type(GLOG_TYPE_STDERR, pname);
 | 
			
		||||
    gutil_log_default.level = GLOG_LEVEL_DEFAULT;
 | 
			
		||||
 | 
			
		||||
    g_option_context_add_main_entries(options, entries, NULL);
 | 
			
		||||
    if (g_option_context_parse(options, &argc, &argv, &error)) {
 | 
			
		||||
        char* help;
 | 
			
		||||
 | 
			
		||||
        opt->dev = g_strdup(DEV_DEFAULT);
 | 
			
		||||
        switch (argc) {
 | 
			
		||||
        case 2:
 | 
			
		||||
            opt->service = argv[1];
 | 
			
		||||
            /* no break */
 | 
			
		||||
        case 1:
 | 
			
		||||
            ok = TRUE;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            help = g_option_context_get_help(options, TRUE, NULL);
 | 
			
		||||
            fprintf(stderr, "%s", help);
 | 
			
		||||
            g_free(help);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("%s", error->message);
 | 
			
		||||
        g_error_free(error);
 | 
			
		||||
    }
 | 
			
		||||
    g_option_context_free(options);
 | 
			
		||||
    return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    App app;
 | 
			
		||||
    AppOptions opt;
 | 
			
		||||
 | 
			
		||||
    memset(&app, 0, sizeof(app));
 | 
			
		||||
    app.ret = RET_INVARG;
 | 
			
		||||
    app.opt = &opt;
 | 
			
		||||
    if (app_init(&opt, argc, argv)) {
 | 
			
		||||
        app.sm = gbinder_servicemanager_new(opt.dev);
 | 
			
		||||
        if (app.sm) {
 | 
			
		||||
            app_run(&app);
 | 
			
		||||
            gbinder_servicemanager_unref(app.sm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_free(opt.dev);
 | 
			
		||||
    return app.ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -194,7 +194,7 @@ app_init(
 | 
			
		||||
        { "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 | 
			
		||||
          app_log_quiet, "Be quiet", NULL },
 | 
			
		||||
        { "async", 'a', 0, G_OPTION_ARG_NONE, &opt->async,
 | 
			
		||||
          "Parform operations asynchronously", NULL },
 | 
			
		||||
          "Perform operations asynchronously", NULL },
 | 
			
		||||
        { "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
 | 
			
		||||
          "Binder device [" DEV_DEFAULT "]", "DEVICE" },
 | 
			
		||||
        { NULL }
 | 
			
		||||
@@ -245,7 +245,7 @@ int main(int argc, char* argv[])
 | 
			
		||||
    app.opt = &opt;
 | 
			
		||||
    if (app_init(&opt, argc, argv)) {
 | 
			
		||||
        app.sm = gbinder_servicemanager_new(opt.dev);
 | 
			
		||||
        if (app.sm) {
 | 
			
		||||
        if (gbinder_servicemanager_wait(app.sm, -1)) {
 | 
			
		||||
            if (opt.async) {
 | 
			
		||||
                app_async(&app);
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								test/binder-ping/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								test/binder-ping/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
.PHONY: all debug release clean cleaner
 | 
			
		||||
.PHONY: libgbinder-release libgbinder-debug
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Required packages
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Default target
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
all: debug release
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Executable
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
EXE = binder-ping
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# 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)
 | 
			
		||||
							
								
								
									
										187
									
								
								test/binder-ping/binder-ping.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								test/binder-ping/binder-ping.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#define RET_OK          (0)
 | 
			
		||||
#define RET_NOTFOUND    (1)
 | 
			
		||||
#define RET_INVARG      (2)
 | 
			
		||||
#define RET_ERR         (3)
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_BINDER          GBINDER_DEFAULT_HWBINDER
 | 
			
		||||
#define AIDL_PING_TRANSACTION   GBINDER_FOURCC('_','P','N','G')
 | 
			
		||||
#define HIDL_PING_TRANSACTION   GBINDER_FOURCC(0x0f,'P','N','G')
 | 
			
		||||
 | 
			
		||||
typedef struct app_options {
 | 
			
		||||
    const char* fqname;
 | 
			
		||||
    char* dev;
 | 
			
		||||
    guint32 ping_code;
 | 
			
		||||
    const char* iface;
 | 
			
		||||
} AppOptions;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
app_run(
 | 
			
		||||
    const AppOptions* opt)
 | 
			
		||||
{
 | 
			
		||||
    int ret = RET_NOTFOUND;
 | 
			
		||||
    GBinderServiceManager* sm = gbinder_servicemanager_new(opt->dev);
 | 
			
		||||
 | 
			
		||||
    if (sm) {
 | 
			
		||||
        int status = 0;
 | 
			
		||||
        GBinderRemoteObject* remote = gbinder_servicemanager_get_service_sync
 | 
			
		||||
            (sm, opt->fqname, &status);
 | 
			
		||||
 | 
			
		||||
        if (remote) {
 | 
			
		||||
            int status;
 | 
			
		||||
            GBinderClient* client = gbinder_client_new(remote, opt->iface);
 | 
			
		||||
            GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
 | 
			
		||||
                (client, opt->ping_code, NULL, &status);
 | 
			
		||||
                             
 | 
			
		||||
            if (reply) {
 | 
			
		||||
                GINFO("OK");
 | 
			
		||||
                ret = RET_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GERR("Ping failed (%d)", status);
 | 
			
		||||
                ret = RET_ERR;
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
            gbinder_client_unref(client);
 | 
			
		||||
        } else {
 | 
			
		||||
            GERR("%s not found", opt->fqname);
 | 
			
		||||
        }
 | 
			
		||||
        gbinder_servicemanager_unref(sm);
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("No servicemanager at %s", opt->dev);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_log_verbose(
 | 
			
		||||
    const gchar* name,
 | 
			
		||||
    const gchar* value,
 | 
			
		||||
    gpointer data,
 | 
			
		||||
    GError** error)
 | 
			
		||||
{
 | 
			
		||||
    gutil_log_default.level = GLOG_LEVEL_VERBOSE;
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_log_quiet(
 | 
			
		||||
    const gchar* name,
 | 
			
		||||
    const gchar* value,
 | 
			
		||||
    gpointer data,
 | 
			
		||||
    GError** error)
 | 
			
		||||
{
 | 
			
		||||
    gutil_log_default.level = GLOG_LEVEL_NONE;
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_init(
 | 
			
		||||
    AppOptions* opt,
 | 
			
		||||
    int argc,
 | 
			
		||||
    char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    gboolean ok = FALSE;
 | 
			
		||||
    GOptionEntry entries[] = {
 | 
			
		||||
        { "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 | 
			
		||||
          app_log_verbose, "Enable verbose output", NULL },
 | 
			
		||||
        { "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
 | 
			
		||||
          app_log_quiet, "Be quiet", NULL },
 | 
			
		||||
        { "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
 | 
			
		||||
          "Binder device [" DEFAULT_BINDER "]", "DEVICE" },
 | 
			
		||||
        { NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GError* error = NULL;
 | 
			
		||||
    GOptionContext* options = g_option_context_new("[FQNAME]");
 | 
			
		||||
 | 
			
		||||
    gutil_log_timestamp = FALSE;
 | 
			
		||||
    gutil_log_default.level = GLOG_LEVEL_DEFAULT;
 | 
			
		||||
 | 
			
		||||
    g_option_context_add_main_entries(options, entries, NULL);
 | 
			
		||||
    if (g_option_context_parse(options, &argc, &argv, &error)) {
 | 
			
		||||
        if (!opt->dev || !opt->dev[0]) {
 | 
			
		||||
            opt->dev = g_strdup(DEFAULT_BINDER);
 | 
			
		||||
        }
 | 
			
		||||
        if (argc == 2) {
 | 
			
		||||
            opt->fqname = argv[1];
 | 
			
		||||
            if (g_strcmp0(opt->dev, GBINDER_DEFAULT_BINDER)) {
 | 
			
		||||
                opt->ping_code = HIDL_PING_TRANSACTION;
 | 
			
		||||
                opt->iface = "android.hidl.base@1.0::IBase";
 | 
			
		||||
            } else {
 | 
			
		||||
                opt->ping_code = AIDL_PING_TRANSACTION;
 | 
			
		||||
                opt->iface = "android.os.IBinder";
 | 
			
		||||
            }
 | 
			
		||||
            ok = TRUE;
 | 
			
		||||
        } else {
 | 
			
		||||
            char* help = g_option_context_get_help(options, TRUE, NULL);
 | 
			
		||||
 | 
			
		||||
            fprintf(stderr, "%s", help);
 | 
			
		||||
            g_free(help);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        GERR("%s", error->message);
 | 
			
		||||
        g_error_free(error);
 | 
			
		||||
    }
 | 
			
		||||
    g_option_context_free(options);
 | 
			
		||||
    return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    AppOptions opt;
 | 
			
		||||
    int ret = RET_INVARG;
 | 
			
		||||
 | 
			
		||||
    memset(&opt, 0, sizeof(opt));
 | 
			
		||||
    if (app_init(&opt, argc, argv)) {
 | 
			
		||||
        ret = app_run(&opt);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(opt.dev);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Contact: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -36,6 +36,9 @@
 | 
			
		||||
 | 
			
		||||
#include <glib-unix.h>
 | 
			
		||||
 | 
			
		||||
#define BINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4)
 | 
			
		||||
#define BINDER_DUMP_TRANSACTION      BINDER_TRANSACTION('D','M','P')
 | 
			
		||||
 | 
			
		||||
#define RET_OK          (0)
 | 
			
		||||
#define RET_NOTFOUND    (1)
 | 
			
		||||
#define RET_INVARG      (2)
 | 
			
		||||
@@ -49,6 +52,7 @@ typedef struct app_options {
 | 
			
		||||
    char* dev;
 | 
			
		||||
    char* iface;
 | 
			
		||||
    const char* name;
 | 
			
		||||
    gboolean async;
 | 
			
		||||
} AppOptions;
 | 
			
		||||
 | 
			
		||||
typedef struct app {
 | 
			
		||||
@@ -59,6 +63,11 @@ typedef struct app {
 | 
			
		||||
    int ret;
 | 
			
		||||
} App;
 | 
			
		||||
 | 
			
		||||
typedef struct response {
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
} Response;
 | 
			
		||||
 | 
			
		||||
static const char pname[] = "binder-service";
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -73,6 +82,29 @@ app_signal(
 | 
			
		||||
    return G_SOURCE_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
app_async_resp(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    Response* resp = user_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_complete(resp->req, resp->reply, 0);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_async_free(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    Response* resp = user_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_unref(resp->reply);
 | 
			
		||||
    gbinder_remote_request_unref(resp->req);
 | 
			
		||||
    g_free(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
app_reply(
 | 
			
		||||
@@ -83,15 +115,52 @@ app_reply(
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    char* str = gbinder_remote_request_read_string16(req);
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
    App* app = user_data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    GVERBOSE("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    GDEBUG("\"%s\"", str);
 | 
			
		||||
    gbinder_local_reply_append_string16(reply, str);
 | 
			
		||||
    g_free(str);
 | 
			
		||||
    *status = 0;
 | 
			
		||||
    return reply;
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    if (code == GBINDER_FIRST_CALL_TRANSACTION) {
 | 
			
		||||
        const AppOptions* opt = app->opt;
 | 
			
		||||
        const char* iface = gbinder_remote_request_interface(req);
 | 
			
		||||
 | 
			
		||||
        if (!g_strcmp0(iface, opt->iface)) {
 | 
			
		||||
            char* str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
            GBinderLocalReply* reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
 | 
			
		||||
            GVERBOSE("\"%s\" %u", iface, code);
 | 
			
		||||
            GDEBUG("\"%s\"", str);
 | 
			
		||||
            *status = 0;
 | 
			
		||||
            gbinder_local_reply_append_string16(reply, str);
 | 
			
		||||
            g_free(str);
 | 
			
		||||
            if (opt->async) {
 | 
			
		||||
                Response* resp = g_new0(Response, 1);
 | 
			
		||||
 | 
			
		||||
                resp->reply = reply;
 | 
			
		||||
                resp->req = gbinder_remote_request_ref(req);
 | 
			
		||||
                g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, app_async_resp,
 | 
			
		||||
                    resp, app_async_free);
 | 
			
		||||
                gbinder_remote_request_block(resp->req);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            } else {
 | 
			
		||||
                return reply;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
             GDEBUG("Unexpected interface \"%s\"", iface);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (code == BINDER_DUMP_TRANSACTION) {
 | 
			
		||||
        int fd = gbinder_reader_read_fd(&reader);
 | 
			
		||||
        const char* dump = "Sorry, I've got nothing to dump...\n";
 | 
			
		||||
        const gssize dump_len = strlen(dump);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Dump request from %d", gbinder_remote_request_sender_pid(req));
 | 
			
		||||
        if (write(fd, dump, dump_len) != dump_len) {
 | 
			
		||||
            GERR("Failed to write dump: %s", strerror(errno));
 | 
			
		||||
        }
 | 
			
		||||
        *status = 0;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -112,6 +181,23 @@ app_add_service_done(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_sm_presence_handler(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    App* app = user_data;
 | 
			
		||||
 | 
			
		||||
    if (gbinder_servicemanager_is_present(app->sm)) {
 | 
			
		||||
        GINFO("Service manager has reappeared");
 | 
			
		||||
        gbinder_servicemanager_add_service(app->sm, app->opt->name, app->obj,
 | 
			
		||||
            app_add_service_done, app);
 | 
			
		||||
    } else {
 | 
			
		||||
        GINFO("Service manager has died");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
app_run(
 | 
			
		||||
@@ -120,6 +206,8 @@ app_run(
 | 
			
		||||
    const char* name = app->opt->name;
 | 
			
		||||
    guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app);
 | 
			
		||||
    guint sigint = g_unix_signal_add(SIGINT, app_signal, app);
 | 
			
		||||
    gulong presence_id = gbinder_servicemanager_add_presence_handler
 | 
			
		||||
        (app->sm, app_sm_presence_handler, app);
 | 
			
		||||
 | 
			
		||||
    app->loop = g_main_loop_new(NULL, TRUE);
 | 
			
		||||
 | 
			
		||||
@@ -130,6 +218,7 @@ app_run(
 | 
			
		||||
 | 
			
		||||
    if (sigtrm) g_source_remove(sigtrm);
 | 
			
		||||
    if (sigint) g_source_remove(sigint);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(app->sm, presence_id);
 | 
			
		||||
    g_main_loop_unref(app->loop);
 | 
			
		||||
    app->loop = NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -175,6 +264,8 @@ app_init(
 | 
			
		||||
          "Binder device [" DEFAULT_DEVICE "]", "DEVICE" },
 | 
			
		||||
        { "interface", 'i', 0, G_OPTION_ARG_STRING, &opt->iface,
 | 
			
		||||
          "Local interface [" DEFAULT_IFACE "]", "IFACE" },
 | 
			
		||||
        { "async", 'a', 0, G_OPTION_ARG_NONE, &opt->async,
 | 
			
		||||
          "Handle calls asynchronously", NULL },
 | 
			
		||||
        { NULL }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -226,7 +317,7 @@ int main(int argc, char* argv[])
 | 
			
		||||
    app.opt = &opt;
 | 
			
		||||
    if (app_init(&opt, argc, argv)) {
 | 
			
		||||
        app.sm = gbinder_servicemanager_new(opt.dev);
 | 
			
		||||
        if (app.sm) {
 | 
			
		||||
        if (gbinder_servicemanager_wait(app.sm, -1)) {
 | 
			
		||||
            app.obj = gbinder_servicemanager_new_local_object
 | 
			
		||||
                (app.sm, opt.iface, app_reply, &app);
 | 
			
		||||
            app_run(&app);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
all:
 | 
			
		||||
%:
 | 
			
		||||
	@$(MAKE) -C unit_buffer $*
 | 
			
		||||
	@$(MAKE) -C unit_cleanup $*
 | 
			
		||||
	@$(MAKE) -C unit_client $*
 | 
			
		||||
	@$(MAKE) -C unit_driver $*
 | 
			
		||||
	@$(MAKE) -C unit_ipc $*
 | 
			
		||||
@@ -14,6 +15,9 @@ all:
 | 
			
		||||
	@$(MAKE) -C unit_remote_object $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_reply $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_request $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager $*
 | 
			
		||||
	@$(MAKE) -C unit_servicename $*
 | 
			
		||||
	@$(MAKE) -C unit_servicepoll $*
 | 
			
		||||
	@$(MAKE) -C unit_writer $*
 | 
			
		||||
 | 
			
		||||
clean: unitclean
 | 
			
		||||
 
 | 
			
		||||
@@ -138,10 +138,10 @@ test_banner:
 | 
			
		||||
	@echo "===========" $(EXE) "=========== "
 | 
			
		||||
 | 
			
		||||
test: test_banner debug 
 | 
			
		||||
	@LD_LIBRARY_PATH="$(LIB_DIR)/$(DEBUG_LIB_PATH)" $(DEBUG_EXE)
 | 
			
		||||
	@$(DEBUG_EXE)
 | 
			
		||||
 | 
			
		||||
valgrind: test_banner debug
 | 
			
		||||
	@LD_LIBRARY_PATH="$(LIB_DIR)/$(DEBUG_LIB_PATH)" G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --tool=memcheck --leak-check=full --show-possibly-lost=no $(DEBUG_EXE)
 | 
			
		||||
	@G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --tool=memcheck --leak-check=full --show-possibly-lost=no $(DEBUG_EXE)
 | 
			
		||||
 | 
			
		||||
$(DEBUG_BUILD_DIR):
 | 
			
		||||
	mkdir -p $@
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -45,6 +45,7 @@ GLOG_MODULE_DEFINE2("test_binder", gutil_log_default);
 | 
			
		||||
 | 
			
		||||
static GHashTable* test_fd_map = NULL;
 | 
			
		||||
static GHashTable* test_node_map = NULL;
 | 
			
		||||
static GPrivate test_looper;
 | 
			
		||||
 | 
			
		||||
#define public_fd  fd[0]
 | 
			
		||||
#define private_fd fd[1]
 | 
			
		||||
@@ -58,6 +59,22 @@ static GHashTable* test_node_map = NULL;
 | 
			
		||||
#define TF_ACCEPT_FDS  0x10
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_io TestBinderIo;
 | 
			
		||||
typedef struct test_binder TestBinder;
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*TestBinderPushDataFunc)(
 | 
			
		||||
    int fd,
 | 
			
		||||
    const void* data);
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_submit_thread {
 | 
			
		||||
    GThread* thread;
 | 
			
		||||
    GCond cond;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    gboolean run;
 | 
			
		||||
    GSList* queue;
 | 
			
		||||
    TestBinder* binder;
 | 
			
		||||
} TestBinderSubmitThread;
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_node {
 | 
			
		||||
    char* path;
 | 
			
		||||
@@ -68,6 +85,8 @@ typedef struct test_binder_node {
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder {
 | 
			
		||||
    TestBinderNode* node;
 | 
			
		||||
    TestBinderSubmitThread* submit_thread;
 | 
			
		||||
    gboolean looper_enabled;
 | 
			
		||||
    int fd[2];
 | 
			
		||||
} TestBinder;
 | 
			
		||||
 | 
			
		||||
@@ -104,6 +123,11 @@ typedef struct binder_pre_cookie_64 {
 | 
			
		||||
    guint64 cookie;
 | 
			
		||||
} BinderPtrCookie64;
 | 
			
		||||
 | 
			
		||||
typedef struct binder_handle_cookie_64 {
 | 
			
		||||
  guint32 handle;
 | 
			
		||||
  guint64 cookie;
 | 
			
		||||
} __attribute__((packed)) BinderHandleCookie64;
 | 
			
		||||
 | 
			
		||||
#define BC_TRANSACTION_64       _IOW('c', 0, BinderTransactionData64)
 | 
			
		||||
#define BC_REPLY_64             _IOW('c', 1, BinderTransactionData64)
 | 
			
		||||
#define BC_FREE_BUFFER_64       _IOW('c', 3, guint64)
 | 
			
		||||
@@ -113,6 +137,8 @@ typedef struct binder_pre_cookie_64 {
 | 
			
		||||
#define BC_DECREFS              _IOW('c', 7, guint32)
 | 
			
		||||
#define BC_ENTER_LOOPER          _IO('c', 12)
 | 
			
		||||
#define BC_EXIT_LOOPER           _IO('c', 13)
 | 
			
		||||
#define BC_REQUEST_DEATH_NOTIFICATION_64 _IOW('c', 14, BinderHandleCookie64)
 | 
			
		||||
#define BC_CLEAR_DEATH_NOTIFICATION_64   _IOW('c', 15, BinderHandleCookie64)
 | 
			
		||||
 | 
			
		||||
#define BR_TRANSACTION_64       _IOR('r', 2, BinderTransactionData64)
 | 
			
		||||
#define BR_REPLY_64             _IOR('r', 3, BinderTransactionData64)
 | 
			
		||||
@@ -126,6 +152,110 @@ typedef struct binder_pre_cookie_64 {
 | 
			
		||||
#define BR_DEAD_BINDER_64       _IOR('r', 15, guint64)
 | 
			
		||||
#define BR_FAILED_REPLY          _IO('r', 17)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gpointer
 | 
			
		||||
test_binder_submit_thread_proc(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderSubmitThread* submit = data;
 | 
			
		||||
    TestBinder* binder = submit->binder;
 | 
			
		||||
    GMutex* mutex = &submit->mutex;
 | 
			
		||||
    GCond* cond = &submit->cond;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Submit thread started");
 | 
			
		||||
    g_mutex_lock(mutex);
 | 
			
		||||
    while (submit->run) {
 | 
			
		||||
        GBytes* next = NULL;
 | 
			
		||||
 | 
			
		||||
        while (submit->run && !next) {
 | 
			
		||||
            if (submit->queue) {
 | 
			
		||||
                next = submit->queue->data;
 | 
			
		||||
                submit->queue = g_slist_remove(submit->queue, next);
 | 
			
		||||
                break;
 | 
			
		||||
            } else {
 | 
			
		||||
                g_cond_wait(cond, mutex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (next) {
 | 
			
		||||
            int bytes_available = 0;
 | 
			
		||||
            int err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
 | 
			
		||||
 | 
			
		||||
            /* Wait until the queue is empty */
 | 
			
		||||
            g_assert(err >= 0);
 | 
			
		||||
            while (bytes_available > 0 && submit->run) {
 | 
			
		||||
                /* Wait a bit between polls */
 | 
			
		||||
                g_cond_wait_until(cond, mutex, g_get_monotonic_time () +
 | 
			
		||||
                    100 * G_TIME_SPAN_MILLISECOND);
 | 
			
		||||
                err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
 | 
			
		||||
                g_assert(err >= 0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (submit->run) {
 | 
			
		||||
                gsize len;
 | 
			
		||||
                gconstpointer data = g_bytes_get_data(next, &len);
 | 
			
		||||
 | 
			
		||||
                GDEBUG("Submitting command 0x%08x", *(guint32*)data);
 | 
			
		||||
                g_assert(write(binder->private_fd, data, len) == len);
 | 
			
		||||
            }
 | 
			
		||||
            g_bytes_unref(next);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(mutex);
 | 
			
		||||
    GDEBUG("Submit thread exiting");
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestBinderSubmitThread*
 | 
			
		||||
test_binder_submit_thread_new(
 | 
			
		||||
    TestBinder* binder)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderSubmitThread* submit = g_new0(TestBinderSubmitThread, 1);
 | 
			
		||||
 | 
			
		||||
    submit->run = TRUE;
 | 
			
		||||
    submit->binder = binder;
 | 
			
		||||
    g_cond_init(&submit->cond);
 | 
			
		||||
    g_mutex_init(&submit->mutex);
 | 
			
		||||
    submit->thread = g_thread_new(binder->node->path,
 | 
			
		||||
        test_binder_submit_thread_proc, submit);
 | 
			
		||||
    return submit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_submit_thread_free(
 | 
			
		||||
    TestBinderSubmitThread* submit)
 | 
			
		||||
{
 | 
			
		||||
    if (submit) {
 | 
			
		||||
        g_mutex_lock(&submit->mutex);
 | 
			
		||||
        submit->run = FALSE;
 | 
			
		||||
        g_cond_signal(&submit->cond);
 | 
			
		||||
        g_mutex_unlock(&submit->mutex);
 | 
			
		||||
        g_thread_join(submit->thread);
 | 
			
		||||
 | 
			
		||||
        g_slist_free_full(submit->queue, (GDestroyNotify) g_bytes_unref);
 | 
			
		||||
        g_cond_clear(&submit->cond);
 | 
			
		||||
        g_mutex_clear(&submit->mutex);
 | 
			
		||||
        g_free(submit);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_submit_later(
 | 
			
		||||
    TestBinderSubmitThread* submit,
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    const guint32* cmd = data;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&submit->mutex);
 | 
			
		||||
    submit->queue = g_slist_append(submit->queue,
 | 
			
		||||
        g_bytes_new(cmd, sizeof(*cmd) + _IOC_SIZE(*cmd)));
 | 
			
		||||
    g_cond_signal(&submit->cond);
 | 
			
		||||
    g_mutex_unlock(&submit->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_io_free_buffer(
 | 
			
		||||
@@ -151,11 +281,11 @@ test_io_handle_write_read_64(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    int err, bytes_available = 0;
 | 
			
		||||
    BinderWriteRead64* wr = data;
 | 
			
		||||
    gssize bytes_left = wr->write_size - wr->write_consumed;
 | 
			
		||||
    const guint8* write_ptr = (void*)(gsize)
 | 
			
		||||
        (wr->write_buffer + wr->write_consumed);
 | 
			
		||||
    gboolean is_looper;
 | 
			
		||||
 | 
			
		||||
    while (bytes_left >= sizeof(guint32)) {
 | 
			
		||||
        const guint cmd = *(guint32*)write_ptr;
 | 
			
		||||
@@ -176,12 +306,18 @@ test_io_handle_write_read_64(
 | 
			
		||||
                test_io_free_buffer(binder,
 | 
			
		||||
                    GSIZE_TO_POINTER(*(guint64*)write_ptr));
 | 
			
		||||
                break;
 | 
			
		||||
            case BC_ENTER_LOOPER:
 | 
			
		||||
                 g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
 | 
			
		||||
                 break;
 | 
			
		||||
            case BC_EXIT_LOOPER:
 | 
			
		||||
                 g_private_set(&test_looper, NULL);
 | 
			
		||||
                 break;
 | 
			
		||||
            case BC_REQUEST_DEATH_NOTIFICATION_64:
 | 
			
		||||
            case BC_CLEAR_DEATH_NOTIFICATION_64:
 | 
			
		||||
            case BC_INCREFS:
 | 
			
		||||
            case BC_ACQUIRE:
 | 
			
		||||
            case BC_RELEASE:
 | 
			
		||||
            case BC_DECREFS:
 | 
			
		||||
            case BC_ENTER_LOOPER:
 | 
			
		||||
            case BC_EXIT_LOOPER:
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
#pragma message("TODO: implement more BINDER_WRITE_READ commands")
 | 
			
		||||
@@ -198,24 +334,45 @@ test_io_handle_write_read_64(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now read the data from the socket */
 | 
			
		||||
    err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
 | 
			
		||||
    if (err >= 0) {
 | 
			
		||||
        int bytes_read = 0;
 | 
			
		||||
        if (bytes_available > 0) {
 | 
			
		||||
            bytes_read = read(binder->public_fd,
 | 
			
		||||
                (void*)(gsize)(wr->read_buffer + wr->read_consumed),
 | 
			
		||||
                wr->read_size - wr->read_consumed);
 | 
			
		||||
        }
 | 
			
		||||
    is_looper = g_private_get(&test_looper) ? TRUE : FALSE;
 | 
			
		||||
    if (binder->looper_enabled || !is_looper) {
 | 
			
		||||
        /* Now read the data from the socket */
 | 
			
		||||
        int bytes_available = 0;
 | 
			
		||||
        int err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
 | 
			
		||||
 | 
			
		||||
        if (bytes_read >= 0) {
 | 
			
		||||
            wr->read_consumed += bytes_read;
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            err = bytes_read;
 | 
			
		||||
        if (err >= 0) {
 | 
			
		||||
            int bytes_read = 0;
 | 
			
		||||
 | 
			
		||||
            if (bytes_available >= 4) {
 | 
			
		||||
                bytes_read = read(binder->public_fd,
 | 
			
		||||
                    (void*)(gsize)(wr->read_buffer + wr->read_consumed),
 | 
			
		||||
                    wr->read_size - wr->read_consumed);
 | 
			
		||||
            } else {
 | 
			
		||||
                struct timespec wait;
 | 
			
		||||
 | 
			
		||||
                wait.tv_sec = 0;
 | 
			
		||||
                wait.tv_nsec = 10 * 1000000; /* 10 ms */
 | 
			
		||||
                nanosleep(&wait, &wait);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (bytes_read >= 0) {
 | 
			
		||||
                wr->read_consumed += bytes_read;
 | 
			
		||||
                return 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                err = bytes_read;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return err;
 | 
			
		||||
    } else {
 | 
			
		||||
        if (wr->read_size > 0) {
 | 
			
		||||
            struct timespec wait;
 | 
			
		||||
 | 
			
		||||
            wait.tv_sec = 0;
 | 
			
		||||
            wait.tv_nsec = 100 * 1000000; /* 100 ms */
 | 
			
		||||
            nanosleep(&wait, &wait);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TestBinderIo test_io_64 = {
 | 
			
		||||
@@ -274,6 +431,17 @@ test_io_destroy_none(
 | 
			
		||||
    GDEBUG("Not freeing %p", data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_looper_enabled(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean enabled)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    binder->looper_enabled = enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_destroy(
 | 
			
		||||
    int fd,
 | 
			
		||||
@@ -291,43 +459,35 @@ test_binder_set_destroy(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_push_data(
 | 
			
		||||
    int fd,
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    const guint32* cmd = data;
 | 
			
		||||
    const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    g_assert(write(binder->private_fd, data, len) == len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_push_data_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    const void* data)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    if (binder) {
 | 
			
		||||
        const guint32* cmd = data;
 | 
			
		||||
        const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
 | 
			
		||||
 | 
			
		||||
        return write(binder->private_fd, data, len) == len;
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    if (!binder->submit_thread) {
 | 
			
		||||
        binder->submit_thread = test_binder_submit_thread_new(binder);
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
    test_binder_submit_later(binder->submit_thread, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_fill_transaction_data(
 | 
			
		||||
    BinderTransactionData64* tr,
 | 
			
		||||
    guint64 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(bytes);
 | 
			
		||||
 | 
			
		||||
    memset(tr, 0, sizeof(*tr));
 | 
			
		||||
    tr->handle = handle;
 | 
			
		||||
    tr->code = code;
 | 
			
		||||
    tr->data_size = bytes->len;
 | 
			
		||||
    tr->sender_pid = getpid();
 | 
			
		||||
    tr->sender_euid = geteuid();
 | 
			
		||||
    /* This memory should eventually get deallocated with BC_FREE_BUFFER_64 */
 | 
			
		||||
    tr->data_buffer = (gsize)g_memdup(bytes->data, bytes->len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
test_binder_push_ptr_cookie(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint32 cmd,
 | 
			
		||||
@@ -339,60 +499,69 @@ test_binder_push_ptr_cookie(
 | 
			
		||||
    memcpy(buf, &cmd, sizeof(cmd));
 | 
			
		||||
    memset(data, 0, sizeof(*data));
 | 
			
		||||
    data->ptr = (gsize)ptr;
 | 
			
		||||
    return test_binder_push_data(fd, buf);
 | 
			
		||||
    test_binder_push_data(fd, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_noop(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_NOOP;
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, &cmd);
 | 
			
		||||
    test_binder_push_data(fd, &cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_increfs(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr)
 | 
			
		||||
{
 | 
			
		||||
    return test_binder_push_ptr_cookie(fd, BR_INCREFS_64, ptr);
 | 
			
		||||
    test_binder_push_ptr_cookie(fd, BR_INCREFS_64, ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_acquire(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr)
 | 
			
		||||
{
 | 
			
		||||
    return test_binder_push_ptr_cookie(fd, BR_ACQUIRE_64, ptr);
 | 
			
		||||
    test_binder_push_ptr_cookie(fd, BR_ACQUIRE_64, ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_release(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr)
 | 
			
		||||
{
 | 
			
		||||
    return test_binder_push_ptr_cookie(fd, BR_RELEASE_64, ptr);
 | 
			
		||||
    test_binder_push_ptr_cookie(fd, BR_RELEASE_64, ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_decrefs(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr)
 | 
			
		||||
{
 | 
			
		||||
    return test_binder_push_ptr_cookie(fd, BR_DECREFS_64, ptr);
 | 
			
		||||
    test_binder_push_ptr_cookie(fd, BR_DECREFS_64, ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction_complete(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_TRANSACTION_COMPLETE;
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, &cmd);
 | 
			
		||||
    test_binder_push_data(fd, &cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction_complete_later(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_TRANSACTION_COMPLETE;
 | 
			
		||||
 | 
			
		||||
    test_binder_push_data_later(fd, &cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_dead_binder(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint handle)
 | 
			
		||||
@@ -403,28 +572,47 @@ test_binder_br_dead_binder(
 | 
			
		||||
    buf[0] = BR_DEAD_BINDER_64;
 | 
			
		||||
    memcpy(buf + 1, &handle64, sizeof(handle64));
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, buf);
 | 
			
		||||
    test_binder_push_data(fd, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_dead_reply(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_DEAD_REPLY;
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, &cmd);
 | 
			
		||||
    test_binder_push_data(fd, &cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_failed_reply(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_FAILED_REPLY;
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, &cmd);
 | 
			
		||||
    test_binder_push_data(fd, &cmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_fill_transaction_data(
 | 
			
		||||
    BinderTransactionData64* tr,
 | 
			
		||||
    guint64 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes)
 | 
			
		||||
{
 | 
			
		||||
    memset(tr, 0, sizeof(*tr));
 | 
			
		||||
    tr->handle = handle;
 | 
			
		||||
    tr->code = code;
 | 
			
		||||
    tr->data_size = bytes ? bytes->len : 0;
 | 
			
		||||
    tr->sender_pid = getpid();
 | 
			
		||||
    tr->sender_euid = geteuid();
 | 
			
		||||
    /* This memory should eventually get deallocated with BC_FREE_BUFFER_64 */
 | 
			
		||||
    tr->data_buffer = (gsize)g_memdup(bytes ? (void*)bytes->data : (void*)"",
 | 
			
		||||
        tr->data_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* target,
 | 
			
		||||
@@ -438,15 +626,17 @@ test_binder_br_transaction(
 | 
			
		||||
    test_binder_fill_transaction_data((void*)(buf + sizeof(cmd)),
 | 
			
		||||
        (gsize)target, code, bytes);
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, buf);
 | 
			
		||||
    test_binder_push_data(fd, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
test_binder_br_reply(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply1(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes)
 | 
			
		||||
    const GByteArray* bytes,
 | 
			
		||||
    TestBinderPushDataFunc push)
 | 
			
		||||
{
 | 
			
		||||
    guint32 cmd = BR_REPLY_64;
 | 
			
		||||
    guint8 buf[sizeof(guint32) + sizeof(BinderTransactionData64)];
 | 
			
		||||
@@ -455,13 +645,34 @@ test_binder_br_reply(
 | 
			
		||||
    test_binder_fill_transaction_data((void*)(buf + sizeof(cmd)),
 | 
			
		||||
        handle, code, bytes);
 | 
			
		||||
 | 
			
		||||
    return test_binder_push_data(fd, buf);
 | 
			
		||||
    push(fd, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
test_binder_br_reply_status(
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status)
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_br_reply1(fd, handle, code, bytes, test_binder_push_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_br_reply1(fd, handle, code, bytes, test_binder_push_data_later);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_status1(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status,
 | 
			
		||||
    TestBinderPushDataFunc push)
 | 
			
		||||
{
 | 
			
		||||
    guint8 buf[sizeof(guint32) + sizeof(BinderTransactionData64)];
 | 
			
		||||
    guint32* cmd = (void*)buf;
 | 
			
		||||
@@ -473,7 +684,24 @@ test_binder_br_reply_status(
 | 
			
		||||
    tr->data_size = sizeof(status);
 | 
			
		||||
    /* This memory should eventually get deallocated with BC_FREE_BUFFER_64 */
 | 
			
		||||
    tr->data_buffer = (gsize)g_memdup(&status, sizeof(status));
 | 
			
		||||
    return test_binder_push_data(fd, buf);
 | 
			
		||||
 | 
			
		||||
    push(fd, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_status(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_br_reply_status1(fd, status, test_binder_push_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_status_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_br_reply_status1(fd, status, test_binder_push_data_later);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -532,6 +760,7 @@ gbinder_system_close(
 | 
			
		||||
            g_hash_table_unref(test_fd_map);
 | 
			
		||||
            test_fd_map = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        test_binder_submit_thread_free(binder->submit_thread);
 | 
			
		||||
        test_binder_node_unref(binder->node);
 | 
			
		||||
        close(binder->public_fd);
 | 
			
		||||
        close(binder->private_fd);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,66 +35,87 @@
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_noop(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_increfs(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_acquire(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_release(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_decrefs(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* ptr);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction_complete(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction_complete_later(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_dead_binder(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint handle);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_dead_reply(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_failed_reply(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_transaction(
 | 
			
		||||
    int fd,
 | 
			
		||||
    void* target,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_status(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    const GByteArray* bytes);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_reply_status_later(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gint32 status);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_looper_enabled(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean enabled);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_destroy(
 | 
			
		||||
    int fd,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
 | 
			
		||||
TESTS="\
 | 
			
		||||
unit_buffer \
 | 
			
		||||
unit_cleanup \
 | 
			
		||||
unit_client \
 | 
			
		||||
unit_driver \
 | 
			
		||||
unit_ipc \
 | 
			
		||||
@@ -16,6 +17,9 @@ unit_reader \
 | 
			
		||||
unit_remote_object \
 | 
			
		||||
unit_remote_reply \
 | 
			
		||||
unit_remote_request \
 | 
			
		||||
unit_servicemanager \
 | 
			
		||||
unit_servicename \
 | 
			
		||||
unit_servicepoll \
 | 
			
		||||
unit_writer"
 | 
			
		||||
 | 
			
		||||
function err() {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,19 +47,21 @@ test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(NULL, NULL, 0);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(NULL, NULL, 0, NULL);
 | 
			
		||||
    GBinderBuffer* buf2;
 | 
			
		||||
    gsize size = 1;
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
 | 
			
		||||
    /* No need to reference the driver if there's no data */
 | 
			
		||||
    buf = gbinder_buffer_new(driver, NULL, 0);
 | 
			
		||||
    buf = gbinder_buffer_new(driver, NULL, 0, NULL);
 | 
			
		||||
    g_assert(!gbinder_buffer_driver(buf));
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
 | 
			
		||||
    buf = gbinder_buffer_new_with_parent(NULL, NULL, 0);
 | 
			
		||||
    buf2 = gbinder_buffer_new_with_parent(buf, NULL, 0);
 | 
			
		||||
    g_assert(!gbinder_buffer_objects(buf));
 | 
			
		||||
    g_assert(!gbinder_buffer_objects(buf2));
 | 
			
		||||
    g_assert(!gbinder_buffer_driver(buf));
 | 
			
		||||
    g_assert(!gbinder_buffer_driver(buf2));
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
@@ -67,6 +69,7 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_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));
 | 
			
		||||
@@ -87,14 +90,14 @@ test_parent(
 | 
			
		||||
    void* ptr = g_memdup(data, sizeof(data));
 | 
			
		||||
    gsize size = 0;
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderBuffer* parent = gbinder_buffer_new(driver, ptr, sizeof(data));
 | 
			
		||||
    GBinderBuffer* parent = gbinder_buffer_new(driver, ptr, sizeof(data), NULL);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new_with_parent
 | 
			
		||||
        (parent, ptr, sizeof(data));
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_buffer_driver(buf) == driver);
 | 
			
		||||
    g_assert(gbinder_buffer_io(buf));
 | 
			
		||||
    g_assert(gbinder_buffer_io(buf) == gbinder_driver_io(driver));
 | 
			
		||||
    g_assert(gbinder_buffer_memory(buf));
 | 
			
		||||
    g_assert(gbinder_buffer_contents(buf));
 | 
			
		||||
    g_assert(gbinder_buffer_data(buf, NULL) == ptr);
 | 
			
		||||
    g_assert(gbinder_buffer_data(buf, &size) == ptr);
 | 
			
		||||
    g_assert(size == sizeof(data));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_cleanup/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_cleanup/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_cleanup
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										126
									
								
								unit/unit_cleanup/unit_cleanup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								unit/unit_cleanup/unit_cleanup.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_cleanup.h"
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cleanup_inc(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    (*((int*)data))++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!gbinder_cleanup_add(NULL, NULL, NULL));
 | 
			
		||||
    gbinder_cleanup_free(NULL);
 | 
			
		||||
    gbinder_cleanup_reset(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    int n1 = 0, n2 =0;
 | 
			
		||||
    GBinderCleanup* cleanup = gbinder_cleanup_add(NULL, test_cleanup_inc, &n1);
 | 
			
		||||
 | 
			
		||||
    g_assert(cleanup);
 | 
			
		||||
    g_assert(gbinder_cleanup_add(cleanup, test_cleanup_inc, &n2) == cleanup);
 | 
			
		||||
    gbinder_cleanup_free(cleanup);
 | 
			
		||||
    g_assert(n1 == 1);
 | 
			
		||||
    g_assert(n2 == 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * reset
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_reset(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    int n1 = 0, n2 =0;
 | 
			
		||||
    GBinderCleanup* cleanup = gbinder_cleanup_add(NULL, test_cleanup_inc, &n1);
 | 
			
		||||
 | 
			
		||||
    g_assert(cleanup);
 | 
			
		||||
    g_assert(gbinder_cleanup_add(cleanup, test_cleanup_inc, &n2) == cleanup);
 | 
			
		||||
    gbinder_cleanup_reset(cleanup);
 | 
			
		||||
    g_assert(n1 == 1);
 | 
			
		||||
    g_assert(n2 == 1);
 | 
			
		||||
 | 
			
		||||
    gbinder_cleanup_free(cleanup);
 | 
			
		||||
    g_assert(n1 == 1);
 | 
			
		||||
    g_assert(n2 == 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/cleanup/"
 | 
			
		||||
#define TEST_(t) TEST_PREFIX 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_("reset"), test_reset);
 | 
			
		||||
    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-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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
 | 
			
		||||
@@ -74,16 +74,15 @@ void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderClient* null = NULL;
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_client_new(NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_client_ref(null));
 | 
			
		||||
    gbinder_client_unref(null);
 | 
			
		||||
    g_assert(!gbinder_client_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_client_interface(NULL));
 | 
			
		||||
    gbinder_client_unref(NULL);
 | 
			
		||||
    g_assert(!gbinder_client_new_request(NULL));
 | 
			
		||||
    g_assert(!gbinder_client_transact_sync_reply(null, 0, NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_client_transact_sync_oneway(null, 0, NULL) == (-EINVAL));
 | 
			
		||||
    g_assert(!gbinder_client_transact(null, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
    gbinder_client_cancel(null, 0);
 | 
			
		||||
    g_assert(!gbinder_client_transact_sync_reply(NULL, 0, NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_client_transact_sync_oneway(NULL, 0, NULL) == (-EINVAL));
 | 
			
		||||
    g_assert(!gbinder_client_transact(NULL, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
    gbinder_client_cancel(NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -98,11 +97,12 @@ test_basic(
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
 | 
			
		||||
    GBinderClient* client = gbinder_client_new(obj, "foo");
 | 
			
		||||
    const char* iface = "foo";
 | 
			
		||||
    GBinderClient* client = gbinder_client_new(obj, iface);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_client_new(obj, NULL));
 | 
			
		||||
    g_assert(client);
 | 
			
		||||
    g_assert(gbinder_client_ref(client) == client);
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_client_interface(client), iface));
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
    gbinder_client_cancel(client, 0); /* does nothing */
 | 
			
		||||
 | 
			
		||||
@@ -111,6 +111,64 @@ test_basic(
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * no_header
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_no_header(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderClient* client = test_client_new(0, NULL);
 | 
			
		||||
    int fd = gbinder_driver_fd(gbinder_client_ipc(client)->driver);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    g_assert(gbinder_client_transact_sync_oneway(client, 0, NULL) ==
 | 
			
		||||
        GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * dead
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_dead_done(
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GVERBOSE_("");
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_dead(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint handle = 1;
 | 
			
		||||
    GBinderClient* client = test_client_new(handle, "foo");
 | 
			
		||||
    GBinderRemoteObject* obj = client->remote;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    const int fd = gbinder_driver_fd(gbinder_client_ipc(client)->driver);
 | 
			
		||||
    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_run(&test_opt, loop);
 | 
			
		||||
    g_assert(gbinder_remote_object_is_dead(obj));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_client_transact_sync_reply(client, 0, NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_client_transact_sync_oneway(client, 0, NULL) == -ESTALE);
 | 
			
		||||
    g_assert(!gbinder_client_transact(client, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * sync_oneway
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -164,10 +222,10 @@ test_sync_reply_tx(
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(data);
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply(fd, handle, code, data->bytes));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code, data->bytes);
 | 
			
		||||
 | 
			
		||||
    tx_reply = gbinder_client_transact_sync_reply(client, 0, req, &status);
 | 
			
		||||
    g_assert(tx_reply);
 | 
			
		||||
@@ -265,10 +323,10 @@ test_reply_tx(
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(data);
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply(fd, handle, code, data->bytes));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code, data->bytes);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_client_transact(client, 0, 0, req, done, destroy, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
@@ -326,17 +384,20 @@ test_reply_ok3(
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/client/"
 | 
			
		||||
#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 "basic", test_basic);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "sync_oneway", test_sync_oneway);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "sync_reply", test_sync_reply);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "reply/ok1", test_reply_ok1);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "reply/ok2", test_reply_ok2);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "reply/ok3", test_reply_ok3);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    g_test_add_func(TEST_("dead"), test_dead);
 | 
			
		||||
    g_test_add_func(TEST_("no_header"), test_no_header);
 | 
			
		||||
    g_test_add_func(TEST_("sync_oneway"), test_sync_oneway);
 | 
			
		||||
    g_test_add_func(TEST_("sync_reply"), test_sync_reply);
 | 
			
		||||
    g_test_add_func(TEST_("reply/ok1"), test_reply_ok1);
 | 
			
		||||
    g_test_add_func(TEST_("reply/ok2"), test_reply_ok2);
 | 
			
		||||
    g_test_add_func(TEST_("reply/ok3"), test_reply_ok3);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -91,7 +91,7 @@ test_noop(
 | 
			
		||||
 | 
			
		||||
    g_assert(driver);
 | 
			
		||||
    g_assert(fd >= 0);
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    g_assert(gbinder_driver_poll(driver, NULL) == POLLIN);
 | 
			
		||||
    g_assert(gbinder_driver_read(driver, NULL, NULL) == 0);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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,7 +34,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply_p.h"
 | 
			
		||||
#include "gbinder_local_request_p.h"
 | 
			
		||||
#include "gbinder_object_registry.h"
 | 
			
		||||
@@ -52,6 +52,24 @@
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_unref_ipc(
 | 
			
		||||
    gpointer ipc)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_quit_when_destroyed(
 | 
			
		||||
    gpointer loop,
 | 
			
		||||
    GObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    test_quit_later((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -109,6 +127,48 @@ test_basic(
 | 
			
		||||
 | 
			
		||||
    /* Invalid path */
 | 
			
		||||
    g_assert(!gbinder_ipc_new("invalid path", NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * async_oneway
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_async_oneway_done(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!status);
 | 
			
		||||
    g_assert(!reply);
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_async_oneway(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    id = gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY,
 | 
			
		||||
        req, test_async_oneway_done, NULL, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -125,11 +185,12 @@ test_sync_oneway(
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    g_assert(gbinder_ipc_transact_sync_oneway(ipc, 0, 1, req) ==
 | 
			
		||||
        GBINDER_STATUS_OK);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -157,10 +218,10 @@ test_sync_reply_ok_status(
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(data);
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply(fd, handle, code, data->bytes));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    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);
 | 
			
		||||
    g_assert(tx_reply);
 | 
			
		||||
@@ -173,6 +234,7 @@ test_sync_reply_ok_status(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -205,16 +267,17 @@ test_sync_reply_error(
 | 
			
		||||
    const gint expected_status = GBINDER_STATUS_FAILED;
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply_status(fd, expected_status));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -268,10 +331,10 @@ test_transact_ok(
 | 
			
		||||
    data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(data);
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_transaction_complete(fd));
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply(fd, handle, code, data->bytes));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code, data->bytes);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_ipc_transact(ipc, handle, code, 0, req,
 | 
			
		||||
        test_transact_ok_done, test_transact_ok_destroy, loop);
 | 
			
		||||
@@ -284,6 +347,7 @@ test_transact_ok(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -317,8 +381,8 @@ test_transact_dead(
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_dead_reply(fd));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_dead_reply(fd);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_dead_done,
 | 
			
		||||
        NULL, loop);
 | 
			
		||||
@@ -330,6 +394,7 @@ test_transact_dead(
 | 
			
		||||
    gbinder_ipc_cancel(ipc, id);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -363,8 +428,8 @@ test_transact_failed(
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_failed_reply(fd));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_failed_reply(fd);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_failed_done,
 | 
			
		||||
        NULL, loop);
 | 
			
		||||
@@ -376,6 +441,7 @@ test_transact_failed(
 | 
			
		||||
    gbinder_ipc_cancel(ipc, id);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -411,8 +477,8 @@ test_transact_status(
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    g_assert(test_binder_br_noop(fd));
 | 
			
		||||
    g_assert(test_binder_br_reply_status(fd, EXPECTED_STATUS));
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply_status(fd, EXPECTED_STATUS);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_status_done,
 | 
			
		||||
        NULL, loop);
 | 
			
		||||
@@ -424,6 +490,7 @@ test_transact_status(
 | 
			
		||||
    gbinder_ipc_cancel(ipc, id);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -453,6 +520,7 @@ test_transact_custom(
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
@@ -483,10 +551,44 @@ test_transact_custom2(
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_custom3
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_custom3_exec(
 | 
			
		||||
    const GBinderIpcTx* tx)
 | 
			
		||||
{
 | 
			
		||||
    GVERBOSE_("");
 | 
			
		||||
    gbinder_ipc_unref(tx->ipc);
 | 
			
		||||
    test_quit_later((GMainLoop*)tx->user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_custom3(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    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,
 | 
			
		||||
        NULL, NULL, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Reference to GBinderIpc is released by test_transact_custom3_exec */
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_cancel
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -532,6 +634,7 @@ test_transact_cancel(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -576,6 +679,7 @@ test_transact_cancel2(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -606,24 +710,6 @@ test_transact_incoming_proc(
 | 
			
		||||
    return gbinder_local_object_new_reply(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_transact_unref_ipc(
 | 
			
		||||
    gpointer ipc)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_done(
 | 
			
		||||
    gpointer loop,
 | 
			
		||||
    GObject* ipc)
 | 
			
		||||
{
 | 
			
		||||
    test_quit_later((GMainLoop*)loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_incoming(
 | 
			
		||||
@@ -634,9 +720,10 @@ test_transact_incoming(
 | 
			
		||||
    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 };
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
        (ipc, "test", test_transact_incoming_proc, loop);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_incoming_proc, loop);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
@@ -647,21 +734,23 @@ test_transact_incoming(
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction(fd, obj, 1, data->bytes);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    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_transact_done, loop);
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_idle_add(test_transact_unref_ipc, ipc);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_incoming_status
 | 
			
		||||
 * transact_status_reply
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -695,9 +784,10 @@ test_transact_status_reply(
 | 
			
		||||
    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 };
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
        (ipc, "test", test_transact_status_reply_proc, loop);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_status_reply_proc, loop);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
@@ -708,16 +798,277 @@ 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_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_transact_done, loop);
 | 
			
		||||
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_idle_add(test_transact_unref_ipc, ipc);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_async
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_transact_async_req {
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderRemoteRequest* req;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
} TestTransactAsyncReq;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async_done(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestTransactAsyncReq* test = data;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_unref(test->obj);
 | 
			
		||||
    gbinder_remote_request_unref(test->req);
 | 
			
		||||
    test_quit_later(test->loop);
 | 
			
		||||
    g_free(test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_transact_async_reply(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    TestTransactAsyncReq* test = data;
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_object_new_reply(test->obj);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_complete(test->req, reply, 0);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_transact_async_proc(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    TestTransactAsyncReq* test = g_new(TestTransactAsyncReq, 1);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_pid(req) == getpid());
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
			
		||||
    g_assert(code == 1);
 | 
			
		||||
 | 
			
		||||
    test->obj = gbinder_local_object_ref(obj);
 | 
			
		||||
    test->req = gbinder_remote_request_ref(req);
 | 
			
		||||
    test->loop = (GMainLoop*)loop;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_block(req);
 | 
			
		||||
    gbinder_remote_request_block(req); /* wrong state; has no effect */
 | 
			
		||||
 | 
			
		||||
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_transact_async_reply, test,
 | 
			
		||||
        test_transact_async_done);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    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 };
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_async_proc, loop);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter 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_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);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * transact_async_sync
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_transact_async_sync_proc(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* loop)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
 | 
			
		||||
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_pid(req) == getpid());
 | 
			
		||||
    g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
			
		||||
    g_assert(code == 1);
 | 
			
		||||
 | 
			
		||||
    /* Block and immediately complete the call */
 | 
			
		||||
    gbinder_remote_request_block(req);
 | 
			
		||||
    gbinder_remote_request_complete(req, reply, 0);
 | 
			
		||||
    gbinder_remote_request_complete(req, reply, 0); /* This one is ignored */
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
 | 
			
		||||
    test_quit_later((GMainLoop*)loop);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_transact_async_sync(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    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 };
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
			
		||||
        (ipc, ifaces, test_transact_async_sync_proc, loop);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter 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_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);
 | 
			
		||||
    g_idle_add(test_unref_ipc, ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * drop_remote_refs
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_drop_remote_refs_cb(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GVERBOSE_("%d", obj->strong_refs);
 | 
			
		||||
    g_assert(obj->strong_refs == 1);
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_drop_remote_refs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    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);
 | 
			
		||||
    gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj,
 | 
			
		||||
        test_drop_remote_refs_cb, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_acquire(fd, obj);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    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_exit will drop the remote reference */
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * cancel_on_exit
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cancel_on_exit_not_reached(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    GBinderRemoteReply* reply,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cancel_on_exit(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    const GBinderIo* io = gbinder_driver_io(ipc->driver);
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* This transaction will be cancelled by gbinder_ipc_exit */
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY,
 | 
			
		||||
        req, test_cancel_on_exit_not_reached, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -726,26 +1077,32 @@ test_transact_status_reply(
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/ipc/"
 | 
			
		||||
#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 "basic", test_basic);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "sync_oneway", test_sync_oneway);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "sync_reply_ok", test_sync_reply_ok);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "sync_reply_error", test_sync_reply_error);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_ok", test_transact_ok);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_dead", test_transact_dead);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_failed", test_transact_failed);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_status", test_transact_status);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_custom", test_transact_custom);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_custom2", test_transact_custom2);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_cancel", test_transact_cancel);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_cancel2", test_transact_cancel2);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_incoming", test_transact_incoming);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "transact_status_reply",
 | 
			
		||||
        test_transact_status_reply);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    g_test_add_func(TEST_("async_oneway"), test_async_oneway);
 | 
			
		||||
    g_test_add_func(TEST_("sync_oneway"), test_sync_oneway);
 | 
			
		||||
    g_test_add_func(TEST_("sync_reply_ok"), test_sync_reply_ok);
 | 
			
		||||
    g_test_add_func(TEST_("sync_reply_error"), test_sync_reply_error);
 | 
			
		||||
    g_test_add_func(TEST_("transact_ok"), test_transact_ok);
 | 
			
		||||
    g_test_add_func(TEST_("transact_dead"), test_transact_dead);
 | 
			
		||||
    g_test_add_func(TEST_("transact_failed"), test_transact_failed);
 | 
			
		||||
    g_test_add_func(TEST_("transact_status"), test_transact_status);
 | 
			
		||||
    g_test_add_func(TEST_("transact_custom"), test_transact_custom);
 | 
			
		||||
    g_test_add_func(TEST_("transact_custom2"), test_transact_custom2);
 | 
			
		||||
    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_incoming"), test_transact_incoming);
 | 
			
		||||
    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);
 | 
			
		||||
    g_test_add_func(TEST_("drop_remote_refs"), test_drop_remote_refs);
 | 
			
		||||
    g_test_add_func(TEST_("cancel_on_exit"), test_cancel_on_exit);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -73,7 +73,8 @@ test_reader_data_init_for_reply(
 | 
			
		||||
    GUtilIntArray* offsets = gbinder_output_data_offsets(out);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(out->bytes->data, out->bytes->len), out->bytes->len);
 | 
			
		||||
        g_memdup(out->bytes->data, out->bytes->len),
 | 
			
		||||
        out->bytes->len, NULL);
 | 
			
		||||
 | 
			
		||||
    memset(data, 0, sizeof(*data));
 | 
			
		||||
    data->buffer = buf;
 | 
			
		||||
@@ -149,6 +150,8 @@ void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* const ifaces_foo[] = { "foo", NULL };
 | 
			
		||||
    const char* const ifaces_bar[] = { "bar", NULL };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderLocalObject* foo;
 | 
			
		||||
@@ -158,8 +161,8 @@ test_basic(
 | 
			
		||||
    g_assert(!gbinder_object_registry_get_local(reg, ipc));
 | 
			
		||||
 | 
			
		||||
    /* Create a new local objects */
 | 
			
		||||
    foo = gbinder_ipc_new_local_object(ipc, "foo", NULL, NULL);
 | 
			
		||||
    bar = gbinder_ipc_new_local_object(ipc, "bar", NULL, NULL);
 | 
			
		||||
    foo = gbinder_local_object_new(ipc, ifaces_foo, NULL, NULL);
 | 
			
		||||
    bar = gbinder_local_object_new(ipc, ifaces_bar, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* But ipc is still not a local object! */
 | 
			
		||||
    g_assert(!gbinder_object_registry_get_local(reg, ipc));
 | 
			
		||||
@@ -186,6 +189,150 @@ test_basic(
 | 
			
		||||
    gbinder_local_object_unref(bar);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * ping
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_ping(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderOutputData* out_data;
 | 
			
		||||
    static const guint8 result[] = { 0x00, 0x00, 0x00, 0x00 };
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, NULL,
 | 
			
		||||
        GBINDER_PING_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
 | 
			
		||||
    /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be
 | 
			
		||||
     * handled by handle_looper_transaction() */
 | 
			
		||||
    g_assert(!gbinder_local_object_handle_transaction(obj, req,
 | 
			
		||||
        GBINDER_PING_TRANSACTION, 0, &status));
 | 
			
		||||
    g_assert(status == (-EBADMSG));
 | 
			
		||||
    reply = gbinder_local_object_handle_looper_transaction(obj, req,
 | 
			
		||||
        GBINDER_PING_TRANSACTION, 0, &status);
 | 
			
		||||
    g_assert(reply);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    out_data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(out_data);
 | 
			
		||||
    g_assert(out_data->bytes);
 | 
			
		||||
    g_assert(out_data->bytes->len == sizeof(result));
 | 
			
		||||
    g_assert(!memcmp(out_data->bytes->data, result, sizeof(result)));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_interface(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderOutputData* out_data;
 | 
			
		||||
    static const guint8 result[] = {
 | 
			
		||||
        TEST_INT32_BYTES(1),
 | 
			
		||||
        TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, NULL,
 | 
			
		||||
        GBINDER_INTERFACE_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
 | 
			
		||||
    /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be
 | 
			
		||||
     * handled by handle_looper_transaction() */
 | 
			
		||||
    g_assert(!gbinder_local_object_handle_transaction(obj, req,
 | 
			
		||||
        GBINDER_INTERFACE_TRANSACTION, 0, &status));
 | 
			
		||||
    g_assert(status == (-EBADMSG));
 | 
			
		||||
    reply = gbinder_local_object_handle_looper_transaction(obj, req,
 | 
			
		||||
        GBINDER_INTERFACE_TRANSACTION, 0, &status);
 | 
			
		||||
    g_assert(reply);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    out_data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(out_data);
 | 
			
		||||
    g_assert(out_data->bytes);
 | 
			
		||||
    g_assert(out_data->bytes->len == sizeof(result));
 | 
			
		||||
    g_assert(!memcmp(out_data->bytes->data, result, sizeof(result)));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_ping
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_hidl_ping(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 req_data [] = { TEST_BASE_INTERFACE_HEADER_BYTES };
 | 
			
		||||
    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);
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderOutputData* out_data;
 | 
			
		||||
    static const guint8 result[] = { 0x00, 0x00, 0x00, 0x00 };
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface));
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface,
 | 
			
		||||
        HIDL_PING_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
 | 
			
		||||
    /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be
 | 
			
		||||
     * handled by handle_looper_transaction() */
 | 
			
		||||
    g_assert(!gbinder_local_object_handle_transaction(obj, req,
 | 
			
		||||
        HIDL_PING_TRANSACTION, 0, &status));
 | 
			
		||||
    g_assert(status == (-EBADMSG));
 | 
			
		||||
    reply = gbinder_local_object_handle_looper_transaction(obj, req,
 | 
			
		||||
        HIDL_PING_TRANSACTION, 0, &status);
 | 
			
		||||
    g_assert(reply);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    out_data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(out_data);
 | 
			
		||||
    g_assert(out_data->bytes);
 | 
			
		||||
    g_assert(out_data->bytes->len == sizeof(result));
 | 
			
		||||
    g_assert(!memcmp(out_data->bytes->data, result, sizeof(result)));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get_descriptor
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -195,21 +342,19 @@ void
 | 
			
		||||
test_get_descriptor(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 req_data [] = {
 | 
			
		||||
        TEST_BASE_INTERFACE_HEADER_BYTES
 | 
			
		||||
    };
 | 
			
		||||
    static const guint8 req_data [] = { TEST_BASE_INTERFACE_HEADER_BYTES };
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj =
 | 
			
		||||
        gbinder_ipc_new_local_object(ipc, NULL, NULL, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(req_data, sizeof(req_data)), sizeof(req_data)), NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface));
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface,
 | 
			
		||||
        HIDL_GET_DESCRIPTOR_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
@@ -255,16 +400,18 @@ test_descriptor_chain(
 | 
			
		||||
    };
 | 
			
		||||
    int status = INT_MAX;
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj =
 | 
			
		||||
        gbinder_ipc_new_local_object(ipc, NULL, NULL, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderOutputData* reply_data;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(req_data, sizeof(req_data)), sizeof(req_data)), NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, HIDL_DESCRIPTOR_CHAIN_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface));
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface,
 | 
			
		||||
        HIDL_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
@@ -279,6 +426,11 @@ test_descriptor_chain(
 | 
			
		||||
    g_assert(reply);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    /* Should get 3 buffers - vector, string and its contents */
 | 
			
		||||
    reply_data = gbinder_local_reply_data(reply);
 | 
			
		||||
    g_assert(gbinder_output_data_offsets(reply_data)->count == 3);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(reply_data) == 64);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
@@ -320,13 +472,14 @@ test_custom_iface(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 req_data [] = { CUSTOM_INTERFACE_HEADER_BYTES };
 | 
			
		||||
    const char* const ifaces[] = { custom_iface, NULL };
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object(ipc, custom_iface,
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces,
 | 
			
		||||
        test_custom_iface_handler, &count);
 | 
			
		||||
    GBinderLocalReply* reply;
 | 
			
		||||
    GBinderReaderData reader_data;
 | 
			
		||||
@@ -334,8 +487,9 @@ test_custom_iface(
 | 
			
		||||
    char** strv;
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(req_data, sizeof(req_data)), sizeof(req_data)), NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface,
 | 
			
		||||
        HIDL_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
 | 
			
		||||
    g_assert(gbinder_local_object_can_handle_transaction(obj, custom_iface,
 | 
			
		||||
@@ -425,17 +579,19 @@ test_reply_status(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 req_data [] = { CUSTOM_INTERFACE_HEADER_BYTES };
 | 
			
		||||
    const char* const ifaces[] = { custom_iface, NULL };
 | 
			
		||||
    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);
 | 
			
		||||
    GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object(ipc, custom_iface,
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces,
 | 
			
		||||
        test_reply_status_handler, &count);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(req_data, sizeof(req_data)), sizeof(req_data)), NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
 | 
			
		||||
    /* Execute the custom transaction */
 | 
			
		||||
    g_assert(!gbinder_local_object_handle_transaction(obj, req,
 | 
			
		||||
@@ -469,7 +625,7 @@ test_increfs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
    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);
 | 
			
		||||
@@ -479,7 +635,7 @@ 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_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->weak_refs == 1);
 | 
			
		||||
@@ -511,7 +667,7 @@ test_decrefs(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
    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);
 | 
			
		||||
@@ -522,7 +678,7 @@ 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_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->weak_refs == 0);
 | 
			
		||||
@@ -553,7 +709,7 @@ test_acquire(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
    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);
 | 
			
		||||
@@ -563,7 +719,7 @@ 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_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->strong_refs == 1);
 | 
			
		||||
@@ -595,8 +751,7 @@ test_release(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_ipc_new_local_object
 | 
			
		||||
        (ipc, NULL, NULL, NULL);
 | 
			
		||||
    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);
 | 
			
		||||
    gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj,
 | 
			
		||||
@@ -606,7 +761,7 @@ 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_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(obj->strong_refs == 0);
 | 
			
		||||
@@ -627,6 +782,9 @@ 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 "basic", test_basic);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "ping", test_ping);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "interface", test_interface);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "hidl_ping", test_hidl_ping);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "get_descriptor", test_get_descriptor);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "descriptor_chain", test_descriptor_chain);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "custom_iface", test_custom_iface);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -67,7 +67,7 @@ test_buffer_from_bytes(
 | 
			
		||||
{
 | 
			
		||||
    /* Prevent double free */
 | 
			
		||||
    test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
 | 
			
		||||
    return gbinder_buffer_new(driver, bytes->data, bytes->len);
 | 
			
		||||
    return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -88,7 +88,7 @@ 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, NULL));
 | 
			
		||||
    g_assert(!gbinder_local_reply_new_from_data(NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_cleanup(NULL, NULL, &count);
 | 
			
		||||
    gbinder_local_reply_cleanup(NULL, test_int_inc, &count);
 | 
			
		||||
@@ -137,8 +137,8 @@ void
 | 
			
		||||
test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 };
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(&gbinder_io_32);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
 | 
			
		||||
@@ -313,7 +313,7 @@ void
 | 
			
		||||
test_string16(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const const char input[] = "x";
 | 
			
		||||
    static const char input[] = "x";
 | 
			
		||||
    static const guint8 output[] = {
 | 
			
		||||
        TEST_INT32_BYTES(1),
 | 
			
		||||
        TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
@@ -359,7 +359,7 @@ test_hidl_string(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlString));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
@@ -382,7 +382,7 @@ test_hidl_string_vec(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlVec));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
@@ -400,8 +400,8 @@ test_local_object(
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL);
 | 
			
		||||
    GBinderLocalObject* obj =
 | 
			
		||||
        gbinder_ipc_new_local_object(ipc, "foo", NULL, NULL);
 | 
			
		||||
    const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL };
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* Append a real object (64-bit I/O is used by test_binder.c) */
 | 
			
		||||
    reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
@@ -479,7 +479,7 @@ test_remote_reply(
 | 
			
		||||
 | 
			
		||||
    /* Copy flat structures (no binder objects) */
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, bytes);
 | 
			
		||||
    req2 = gbinder_local_reply_new_from_data(buffer, NULL);
 | 
			
		||||
    req2 = gbinder_local_reply_new_from_data(buffer);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_reply_data(req2);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -65,7 +65,19 @@ test_buffer_from_bytes(
 | 
			
		||||
{
 | 
			
		||||
    /* Prevent double free */
 | 
			
		||||
    test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
 | 
			
		||||
    return gbinder_buffer_new(driver, bytes->data, bytes->len);
 | 
			
		||||
    return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderBuffer*
 | 
			
		||||
test_buffer_from_bytes_and_objects(
 | 
			
		||||
    GBinderDriver* driver,
 | 
			
		||||
    const GByteArray* bytes,
 | 
			
		||||
    void** objects)
 | 
			
		||||
{
 | 
			
		||||
    /* Prevent double free */
 | 
			
		||||
    test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
 | 
			
		||||
    return gbinder_buffer_new(driver, bytes->data, bytes->len, objects);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -82,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, NULL));
 | 
			
		||||
    g_assert(!gbinder_local_request_new_from_data(NULL));
 | 
			
		||||
    gbinder_local_request_unref(NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(NULL, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(NULL, &writer);
 | 
			
		||||
@@ -167,8 +179,8 @@ void
 | 
			
		||||
test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
 | 
			
		||||
@@ -329,7 +341,7 @@ void
 | 
			
		||||
test_string16(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const const char input[] = "x";
 | 
			
		||||
    static const char input[] = "x";
 | 
			
		||||
    static const guint8 output[] = {
 | 
			
		||||
        TEST_INT32_BYTES(1),
 | 
			
		||||
        TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
@@ -375,7 +387,7 @@ test_hidl_string(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlString));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
@@ -398,7 +410,7 @@ test_hidl_string_vec(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlVec));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
@@ -471,14 +483,14 @@ test_remote_request(
 | 
			
		||||
    const GByteArray* bytes;
 | 
			
		||||
    const GByteArray* bytes2;
 | 
			
		||||
    GBinderBuffer* buffer;
 | 
			
		||||
    void* no_obj = NULL;
 | 
			
		||||
    void** no_obj = g_new0(void*, 1);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string8(req, input);
 | 
			
		||||
    bytes = gbinder_local_request_data(req)->bytes;
 | 
			
		||||
 | 
			
		||||
    /* Copy flat structures (no binder objects) */
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, bytes);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, NULL);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_request_data(req2);
 | 
			
		||||
@@ -490,8 +502,8 @@ test_remote_request(
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
 | 
			
		||||
    /* Same thing but with non-NULL (albeit empty) array of objects */
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, bytes);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, &no_obj);
 | 
			
		||||
    buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
 | 
			
		||||
    data2 = gbinder_local_request_data(req2);
 | 
			
		||||
@@ -525,8 +537,9 @@ test_remote_request_obj_validate_data(
 | 
			
		||||
    g_assert(offsets->data[1] == 4 + BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
    g_assert(offsets->data[2] == 4 + 2*BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
    g_assert(bytes->len == 4 + 2*BUFFER_OBJECT_SIZE_64 + BINDER_OBJECT_SIZE_64);
 | 
			
		||||
    /* HidlString + the contents (2 bytes) aligned at 8-byte boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == (sizeof(HidlString)+8));
 | 
			
		||||
    /* GBinderHidlString + the contents (2 bytes) aligned at 8-byte boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) ==
 | 
			
		||||
        (sizeof(GBinderHidlString) + 8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -558,15 +571,15 @@ test_remote_request_obj(
 | 
			
		||||
        objects[i] = bytes->data + offsets->data[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buffer = test_buffer_from_bytes(driver, data->bytes);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer, objects);
 | 
			
		||||
    buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects);
 | 
			
		||||
    req2 = gbinder_local_request_new_from_data(buffer);
 | 
			
		||||
    gbinder_buffer_free(buffer);
 | 
			
		||||
    g_free(objects);
 | 
			
		||||
 | 
			
		||||
    test_remote_request_obj_validate_data(gbinder_local_request_data(req2));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    /* req2 has to be freed first because req owns data */
 | 
			
		||||
    gbinder_local_request_unref(req2);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,22 +91,44 @@ test_device(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * no_header
 | 
			
		||||
 * no_header1
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_no_header(
 | 
			
		||||
test_no_header1(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER), 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, NULL, NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, NULL);
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * no_header2
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_no_header2(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    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_assert(!gbinder_remote_request_interface(req));
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * write_header
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -144,38 +166,39 @@ test_read_header(
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(test->dev), 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(test->header, test->header_size), test->header_size), NULL);
 | 
			
		||||
    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));
 | 
			
		||||
    gbinder_remote_request_unref(req);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 /*==========================================================================*
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/protocol/"
 | 
			
		||||
#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_PREFIX "device", test_device);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "no_header", test_no_header);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_header_tests); i++) {
 | 
			
		||||
        const TestHeaderData* test = test_header_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX, "/", test->name,
 | 
			
		||||
            "/read_header", NULL);
 | 
			
		||||
        char* path;
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_PREFIX, test->name, "/read_header", NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_read_header);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_PREFIX, "/", test->name,
 | 
			
		||||
            "/write_header", NULL);
 | 
			
		||||
 | 
			
		||||
        path = g_strconcat(TEST_PREFIX, test->name, "/write_header", NULL);
 | 
			
		||||
        g_test_add_data_func(path, test, test_write_header);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -50,8 +50,9 @@ void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!gbinder_remote_object_new(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_remote_object_new(NULL, 0, FALSE));
 | 
			
		||||
    g_assert(!gbinder_remote_object_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_object_ipc(NULL));
 | 
			
		||||
    gbinder_remote_object_unref(NULL);
 | 
			
		||||
    g_assert(gbinder_remote_object_is_dead(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_object_add_death_handler(NULL, NULL, NULL));
 | 
			
		||||
@@ -76,11 +77,14 @@ test_basic(
 | 
			
		||||
    g_assert(obj2);
 | 
			
		||||
    g_assert(obj1->handle == 1u);
 | 
			
		||||
    g_assert(obj2->handle == 2u);
 | 
			
		||||
    g_assert(gbinder_remote_object_ipc(obj1) == ipc);
 | 
			
		||||
    g_assert(gbinder_remote_object_ipc(obj2) == ipc);
 | 
			
		||||
    g_assert(!gbinder_remote_object_is_dead(obj1));
 | 
			
		||||
    g_assert(gbinder_remote_object_reanimate(obj1));
 | 
			
		||||
    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) == obj1);
 | 
			
		||||
    g_assert(gbinder_ipc_get_remote_object(ipc, 1, TRUE) == obj1);
 | 
			
		||||
    gbinder_remote_object_unref(obj1); /* Compensate the above reference */
 | 
			
		||||
    gbinder_remote_object_unref(obj1);
 | 
			
		||||
    gbinder_remote_object_unref(obj2);
 | 
			
		||||
@@ -109,11 +113,14 @@ test_dead(
 | 
			
		||||
    const guint handle = 1;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_ipc_get_remote_object(ipc, handle);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderRemoteObject* obj = gbinder_ipc_get_remote_object
 | 
			
		||||
        (ipc, handle, FALSE);
 | 
			
		||||
    gulong id = gbinder_remote_object_add_death_handler
 | 
			
		||||
        (obj, test_dead_done, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_dead_binder(gbinder_driver_fd(ipc->driver), handle);
 | 
			
		||||
    test_binder_br_dead_binder(fd, handle);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    g_assert(gbinder_remote_object_is_dead(obj));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_reply_ref(NULL));
 | 
			
		||||
    gbinder_remote_reply_unref(NULL);
 | 
			
		||||
    gbinder_remote_reply_set_data(NULL, NULL, NULL);
 | 
			
		||||
    gbinder_remote_reply_set_data(NULL, NULL);
 | 
			
		||||
    gbinder_remote_reply_init_reader(NULL, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(gbinder_remote_reply_is_empty(NULL));
 | 
			
		||||
@@ -85,8 +85,8 @@ test_empty(
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data
 | 
			
		||||
        (reply, gbinder_buffer_new(driver, NULL, 0), NULL);
 | 
			
		||||
    gbinder_remote_reply_set_data(reply,
 | 
			
		||||
        gbinder_buffer_new(driver, NULL, 0, NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_remote_reply_is_empty(reply));
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
@@ -132,8 +132,7 @@ test_int32(
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_reply_is_empty(reply));
 | 
			
		||||
    g_assert(gbinder_remote_reply_read_uint32(reply, &out1));
 | 
			
		||||
@@ -163,8 +162,7 @@ test_int64(
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_reply_is_empty(reply));
 | 
			
		||||
    g_assert(gbinder_remote_reply_read_uint64(reply, &out1));
 | 
			
		||||
@@ -192,8 +190,7 @@ test_string8(
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_reply_is_empty(reply));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_reply_read_string8(reply), "bar"));
 | 
			
		||||
@@ -221,8 +218,7 @@ test_string16(
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
        g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_reply_is_empty(reply));
 | 
			
		||||
    str = gbinder_remote_reply_read_string16(reply);
 | 
			
		||||
@@ -264,7 +260,7 @@ test_to_local(
 | 
			
		||||
    /* 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);
 | 
			
		||||
        sizeof(reply_data), objects));
 | 
			
		||||
 | 
			
		||||
    /* Convert to GBinderLocalReply */
 | 
			
		||||
    req2 = gbinder_remote_reply_copy_to_local(req);
 | 
			
		||||
 
 | 
			
		||||
@@ -71,8 +71,10 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_remote_request_ref(NULL));
 | 
			
		||||
    gbinder_remote_request_unref(NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(NULL, NULL, NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(NULL, 0, NULL);
 | 
			
		||||
    gbinder_remote_request_init_reader(NULL, &reader);
 | 
			
		||||
    gbinder_remote_request_block(NULL);
 | 
			
		||||
    gbinder_remote_request_complete(NULL, NULL, 0);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(NULL));
 | 
			
		||||
    g_assert(!gbinder_remote_request_copy_to_local(NULL));
 | 
			
		||||
@@ -100,6 +102,10 @@ test_basic(
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(NULL), 0, 0);
 | 
			
		||||
 | 
			
		||||
    /* These two calls are wrong but won't cause problems: */
 | 
			
		||||
    gbinder_remote_request_block(req);
 | 
			
		||||
    gbinder_remote_request_complete(req, NULL, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(!gbinder_remote_request_interface(req));
 | 
			
		||||
@@ -118,7 +124,7 @@ void
 | 
			
		||||
test_int32(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 request_data [] = {
 | 
			
		||||
    static const guint8 req_data [] = {
 | 
			
		||||
        TEST_RPC_HEADER,
 | 
			
		||||
        TEST_INT32_BYTES(42)
 | 
			
		||||
    };
 | 
			
		||||
@@ -129,9 +135,9 @@ test_int32(
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev), 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(request_data, sizeof(request_data)), sizeof(request_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE));
 | 
			
		||||
    g_assert(gbinder_remote_request_read_uint32(req, &out1));
 | 
			
		||||
@@ -152,7 +158,7 @@ void
 | 
			
		||||
test_int64(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 request_data [] = {
 | 
			
		||||
    static const guint8 req_data [] = {
 | 
			
		||||
        TEST_RPC_HEADER,
 | 
			
		||||
        TEST_INT64_BYTES(42)
 | 
			
		||||
    };
 | 
			
		||||
@@ -163,9 +169,9 @@ test_int64(
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev), 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(request_data, sizeof(request_data)), sizeof(request_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE));
 | 
			
		||||
    g_assert(gbinder_remote_request_read_uint64(req, &out1));
 | 
			
		||||
@@ -186,7 +192,7 @@ void
 | 
			
		||||
test_string8(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 request_data [] = {
 | 
			
		||||
    static const guint8 req_data [] = {
 | 
			
		||||
        TEST_RPC_HEADER,
 | 
			
		||||
        'b', 'a', 'r', 0x00
 | 
			
		||||
    };
 | 
			
		||||
@@ -195,9 +201,9 @@ test_string8(
 | 
			
		||||
    GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev), 0, 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(request_data, sizeof(request_data)), sizeof(request_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE));
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "bar"));
 | 
			
		||||
@@ -215,7 +221,7 @@ void
 | 
			
		||||
test_string16(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 request_data [] = {
 | 
			
		||||
    static const guint8 req_data [] = {
 | 
			
		||||
        TEST_RPC_HEADER,
 | 
			
		||||
        TEST_INT32_BYTES(3),
 | 
			
		||||
        TEST_INT16_BYTES('b'), TEST_INT16_BYTES('a'),
 | 
			
		||||
@@ -227,9 +233,9 @@ test_string16(
 | 
			
		||||
        gbinder_rpc_protocol_for_device(dev), 0, 0);
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver,
 | 
			
		||||
        g_memdup(request_data, sizeof(request_data)), sizeof(request_data)),
 | 
			
		||||
        NULL);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)),
 | 
			
		||||
        sizeof(req_data), NULL));
 | 
			
		||||
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE));
 | 
			
		||||
    str = gbinder_remote_request_read_string16(req);
 | 
			
		||||
@@ -272,8 +278,8 @@ test_to_local(
 | 
			
		||||
 | 
			
		||||
    /* Skip the 32-bit integer */
 | 
			
		||||
    objects[0] = req_data + 4;
 | 
			
		||||
    gbinder_remote_request_set_data(req, gbinder_buffer_new(driver, req_data,
 | 
			
		||||
        sizeof(request_data)), objects);
 | 
			
		||||
    gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
        gbinder_buffer_new(driver, req_data, sizeof(request_data), objects));
 | 
			
		||||
 | 
			
		||||
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										1072
									
								
								unit/unit_servicemanager/unit_servicemanager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1072
									
								
								unit/unit_servicemanager/unit_servicemanager.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5
									
								
								unit/unit_servicename/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicename/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicename
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										500
									
								
								unit/unit_servicename/unit_servicename.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										500
									
								
								unit/unit_servicename/unit_servicename.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,500 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019 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_servicename.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_quit(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    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(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
{
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_reply(fd, 0, 0, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * TestServiceManager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass TestServiceManagerClass;
 | 
			
		||||
typedef struct test_servicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GCond cond;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    char** services;
 | 
			
		||||
    gboolean block_add;
 | 
			
		||||
    int add_result;
 | 
			
		||||
} TestServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define TEST_SERVICEMANAGER_IFACE "android.os.IServiceManager"
 | 
			
		||||
#define TEST_TYPE_SERVICEMANAGER (test_servicemanager_get_type())
 | 
			
		||||
#define TEST_SERVICEMANAGER(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
    TEST_TYPE_SERVICEMANAGER, TestServiceManager)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
{
 | 
			
		||||
    char** ret;
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    ret = g_strdupv(self->services);
 | 
			
		||||
    GDEBUG("%u", gutil_strv_length(ret));
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    *status = (-ENOENT);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    if (!gutil_strv_contains(self->services, name)) {
 | 
			
		||||
        self->services = gutil_strv_add(self->services, name);
 | 
			
		||||
    }
 | 
			
		||||
    while (self->block_add) {
 | 
			
		||||
        g_cond_wait(&self->cond, &self->mutex);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return self->add_result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
test_servicemanager_check_name(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return name ?
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_INVALID :
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_servicemanager_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_init(
 | 
			
		||||
    TestServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    g_cond_init(&self->cond);
 | 
			
		||||
    g_mutex_init(&self->mutex);
 | 
			
		||||
    self->add_result = GBINDER_STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(object);
 | 
			
		||||
 | 
			
		||||
    g_cond_clear(&self->cond);
 | 
			
		||||
    g_mutex_clear(&self->mutex);
 | 
			
		||||
    g_strfreev(self->services);
 | 
			
		||||
    G_OBJECT_CLASS(test_servicemanager_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_class_init(
 | 
			
		||||
    TestServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = TEST_SERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
    klass->list = test_servicemanager_list;
 | 
			
		||||
    klass->get_service = test_servicemanager_get_service;
 | 
			
		||||
    klass->add_service = test_servicemanager_add_service;
 | 
			
		||||
    klass->check_name = test_servicemanager_check_name;
 | 
			
		||||
    klass->watch = test_servicemanager_watch;
 | 
			
		||||
    klass->unwatch = test_servicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_servicename_new(NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicename_new(sm, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicename_ref(NULL));
 | 
			
		||||
    gbinder_servicename_unref(NULL);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderServiceName* sn;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
    g_assert(!gbinder_servicename_new(sm, obj, NULL));
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_servicename_ref(sn) == sn);
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * present
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_present(
 | 
			
		||||
    int add_result)
 | 
			
		||||
{
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    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_result = add_result;
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
 | 
			
		||||
    /* Immediately generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    /* 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);
 | 
			
		||||
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_present_ok(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_present(GBINDER_STATUS_OK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_present_err(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_present(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * not_present
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_not_present(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GBinderServiceName* sn;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    /* This makes presence detection PING fail */
 | 
			
		||||
    test_binder_br_reply_status(fd, -1);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    g_assert(!gbinder_servicemanager_is_present(sm));
 | 
			
		||||
    id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    sn = gbinder_servicename_new(sm, obj, obj_name);
 | 
			
		||||
    g_assert(sn);
 | 
			
		||||
    g_assert(!g_strcmp0(sn->name, obj_name));
 | 
			
		||||
 | 
			
		||||
    /* Make the next presence detection PING succeed */
 | 
			
		||||
    test_binder_br_transaction_complete_later(fd);
 | 
			
		||||
    test_binder_br_reply_later(fd, 0, 0, NULL);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * cancel
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cancel(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* obj_name = "test";
 | 
			
		||||
    const char* const ifaces[] = { "interface", NULL };
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestServiceManager* test;
 | 
			
		||||
    GBinderServiceName* sn;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* Block name add calls */
 | 
			
		||||
    test = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    test->block_add = TRUE;
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
 | 
			
		||||
    /* 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));
 | 
			
		||||
 | 
			
		||||
    /* Immediately generate death notification (need looper for that) */
 | 
			
		||||
    test_binder_br_dead_binder(fd, 0);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Add call is supposed to be cancelled */
 | 
			
		||||
    gbinder_servicename_unref(sn);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
 | 
			
		||||
    /* Unblock pending add */
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    test->block_add = FALSE;
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(test) "/servicename/" test
 | 
			
		||||
 | 
			
		||||
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_("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_("cancel"), test_cancel);
 | 
			
		||||
    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_servicepoll/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicepoll/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicepoll
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										488
									
								
								unit/unit_servicepoll/unit_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								unit/unit_servicepoll/unit_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,488 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 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_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_setup_ping(
 | 
			
		||||
    GBinderIpc* ipc)
 | 
			
		||||
{
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    test_binder_br_reply(fd, 0, 0, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * TestServiceManager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass TestServiceManagerClass;
 | 
			
		||||
typedef struct test_servicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    char** services;
 | 
			
		||||
} TestServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define TEST_SERVICEMANAGER_IFACE "android.os.IServiceManager"
 | 
			
		||||
#define TEST_TYPE_SERVICEMANAGER (test_servicemanager_get_type())
 | 
			
		||||
#define TEST_SERVICEMANAGER(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
    TEST_TYPE_SERVICEMANAGER, TestServiceManager)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
{
 | 
			
		||||
    char** ret;
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    ret = g_strdupv(self->services);
 | 
			
		||||
    GDEBUG("%u", gutil_strv_length(ret));
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    *status = (-ENOENT);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&self->mutex);
 | 
			
		||||
    if (!gutil_strv_contains(self->services, name)) {
 | 
			
		||||
        self->services = gutil_strv_add(self->services, name);
 | 
			
		||||
    }
 | 
			
		||||
    g_mutex_unlock(&self->mutex);
 | 
			
		||||
    return GBINDER_STATUS_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_SERVICEMANAGER_NAME_CHECK
 | 
			
		||||
test_servicemanager_check_name(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return name ?
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_INVALID :
 | 
			
		||||
        GBINDER_SERVICEMANAGER_NAME_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_servicemanager_watch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_unwatch(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_init(
 | 
			
		||||
    TestServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    g_mutex_init(&self->mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(object);
 | 
			
		||||
 | 
			
		||||
    g_mutex_clear(&self->mutex);
 | 
			
		||||
    g_strfreev(self->services);
 | 
			
		||||
    G_OBJECT_CLASS(test_servicemanager_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_class_init(
 | 
			
		||||
    TestServiceManagerClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    klass->iface = TEST_SERVICEMANAGER_IFACE;
 | 
			
		||||
    klass->default_device = GBINDER_DEFAULT_HWBINDER;
 | 
			
		||||
    klass->rpc_protocol = &gbinder_rpc_protocol_binder;
 | 
			
		||||
    klass->list = test_servicemanager_list;
 | 
			
		||||
    klass->get_service = test_servicemanager_get_service;
 | 
			
		||||
    klass->add_service = test_servicemanager_add_service;
 | 
			
		||||
    klass->check_name = test_servicemanager_check_name;
 | 
			
		||||
    klass->watch = test_servicemanager_watch;
 | 
			
		||||
    klass->unwatch = test_servicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_defaultservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_hwservicemanager_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
{
 | 
			
		||||
    return gbinder_servicemanager_new(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * null
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_null(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!gbinder_servicepoll_ref(NULL));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_manager(NULL));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_is_known_name(NULL, ""));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_add_handler(NULL, NULL, NULL));
 | 
			
		||||
    gbinder_servicepoll_remove_handler(NULL, 0);
 | 
			
		||||
    gbinder_servicepoll_unref(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * basic
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_basic(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GBinderServicePoll* weakptr = NULL;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    manager = gbinder_servicemanager_new(dev);
 | 
			
		||||
    poll = gbinder_servicepoll_new(manager, NULL);
 | 
			
		||||
    g_assert(poll);
 | 
			
		||||
    g_assert(gbinder_servicepoll_manager(poll) == manager);
 | 
			
		||||
    g_assert(!gbinder_servicepoll_is_known_name(poll, "foo"));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_add_handler(poll, NULL, NULL));
 | 
			
		||||
    gbinder_servicepoll_remove_handler(poll, 0); /* this does nothing */
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
 | 
			
		||||
    poll = gbinder_servicepoll_new(manager, &weakptr);
 | 
			
		||||
    g_assert(poll == weakptr);
 | 
			
		||||
    g_assert(poll == gbinder_servicepoll_new(manager, &weakptr));
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_unref(manager);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify1
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_proc(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("\"%s\" added", name_added);
 | 
			
		||||
    if (!g_strcmp0(name_added, "foo")) {
 | 
			
		||||
        test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_notify1_foo(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    GDEBUG("adding \"foo\"");
 | 
			
		||||
    test->services = gutil_strv_add(test->services, "foo");
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_notify1_bar(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    GDEBUG("adding \"bar\"");
 | 
			
		||||
    test->services = gutil_strv_add(test->services, "bar");
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify1(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServicePoll* weakptr = NULL;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    TestServiceManager* test;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    manager = gbinder_servicemanager_new(dev);
 | 
			
		||||
    test = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicepoll_interval_ms = 100;
 | 
			
		||||
    poll = gbinder_servicepoll_new(manager, &weakptr);
 | 
			
		||||
    g_timeout_add(2 * gbinder_servicepoll_interval_ms,
 | 
			
		||||
        test_notify1_bar, test);
 | 
			
		||||
    g_timeout_add(4 * gbinder_servicepoll_interval_ms,
 | 
			
		||||
        test_notify1_foo, test);
 | 
			
		||||
 | 
			
		||||
    id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_servicepoll_is_known_name(poll, "foo"));
 | 
			
		||||
    g_assert(gbinder_servicepoll_is_known_name(poll, "bar"));
 | 
			
		||||
    gbinder_servicepoll_remove_handler(poll, id);
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
    g_assert(!weakptr);
 | 
			
		||||
    gbinder_servicemanager_unref(manager);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify2
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_notify2_foo(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    GDEBUG("services = [\"bar\",\"foo\"]");
 | 
			
		||||
    g_strfreev(test->services);
 | 
			
		||||
    test->services = g_strsplit("bar,bar3,foo", ",", -1);
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_notify2_bar(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* test = user_data;
 | 
			
		||||
 | 
			
		||||
    g_mutex_lock(&test->mutex);
 | 
			
		||||
    GDEBUG("services = [\"bar1\",\"bar2\",\"bar3\"]");
 | 
			
		||||
    g_strfreev(test->services);
 | 
			
		||||
    test->services = g_strsplit("bar1,bar2,bar3", ",", -1);
 | 
			
		||||
    g_mutex_unlock(&test->mutex);
 | 
			
		||||
    return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify2(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServicePoll* weakptr = NULL;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    TestServiceManager* test;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    manager = gbinder_servicemanager_new(dev);
 | 
			
		||||
    test = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicepoll_interval_ms = 100;
 | 
			
		||||
    poll = gbinder_servicepoll_new(manager, &weakptr);
 | 
			
		||||
    g_timeout_add(2 * gbinder_servicepoll_interval_ms,
 | 
			
		||||
        test_notify2_bar, test);
 | 
			
		||||
    g_timeout_add(4 * gbinder_servicepoll_interval_ms,
 | 
			
		||||
        test_notify2_foo, test);
 | 
			
		||||
 | 
			
		||||
    /* Reusing test_notify_proc */
 | 
			
		||||
    id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(gbinder_servicepoll_is_known_name(poll, "foo"));
 | 
			
		||||
    g_assert(gbinder_servicepoll_is_known_name(poll, "bar"));
 | 
			
		||||
    g_assert(gbinder_servicepoll_is_known_name(poll, "bar3"));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_is_known_name(poll, "bar1"));
 | 
			
		||||
    g_assert(!gbinder_servicepoll_is_known_name(poll, "bar2"));
 | 
			
		||||
    gbinder_servicepoll_remove_handler(poll, id);
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
    g_assert(!weakptr);
 | 
			
		||||
    gbinder_servicemanager_unref(manager);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * already_there
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_already_there_proc(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!g_strcmp0(name_added, "foo"));
 | 
			
		||||
    test_quit_later((GMainLoop*)user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_already_there(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    GBinderServicePoll* weakptr = NULL;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    TestServiceManager* test;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    test_setup_ping(ipc);
 | 
			
		||||
    manager = gbinder_servicemanager_new(dev);
 | 
			
		||||
    poll = gbinder_servicepoll_new(manager, &weakptr);
 | 
			
		||||
    test = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
    test->services = gutil_strv_add(test->services, "foo");
 | 
			
		||||
    id = gbinder_servicepoll_add_handler(poll, test_already_there_proc, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicepoll_remove_handler(poll, id);
 | 
			
		||||
    gbinder_servicepoll_unref(poll);
 | 
			
		||||
    g_assert(!weakptr);
 | 
			
		||||
    gbinder_servicemanager_unref(manager);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicepoll/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("basic"), test_basic);
 | 
			
		||||
    g_test_add_func(TEST_("notify1"), test_notify1);
 | 
			
		||||
    g_test_add_func(TEST_("notify2"), test_notify2);
 | 
			
		||||
    g_test_add_func(TEST_("already_there"), test_already_there);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -39,6 +39,8 @@
 | 
			
		||||
 | 
			
		||||
#include <gutil_intarray.h>
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
#define BUFFER_OBJECT_SIZE_32 (24)
 | 
			
		||||
@@ -74,8 +76,10 @@ test_null(
 | 
			
		||||
    gbinder_writer_append_string16(&writer, NULL);
 | 
			
		||||
    gbinder_writer_append_string16_len(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_string16_len(&writer, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_string16_utf16(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_bool(NULL, FALSE);
 | 
			
		||||
    gbinder_writer_append_bool(&writer, FALSE);
 | 
			
		||||
    gbinder_writer_append_fd(NULL, 0);
 | 
			
		||||
    gbinder_writer_append_bytes(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_bytes(&writer, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_hidl_vec(NULL, NULL, 0, 0);
 | 
			
		||||
@@ -91,9 +95,53 @@ test_null(
 | 
			
		||||
    gbinder_writer_append_local_object(&writer, NULL);
 | 
			
		||||
    gbinder_writer_append_remote_object(NULL, NULL);
 | 
			
		||||
    gbinder_writer_append_remote_object(&writer, NULL);
 | 
			
		||||
    gbinder_writer_append_byte_array(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_byte_array(&writer, NULL, 0);
 | 
			
		||||
    gbinder_writer_add_cleanup(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_add_cleanup(NULL, g_free, 0);
 | 
			
		||||
    gbinder_writer_overwrite_int32(NULL, 0, 0);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(NULL));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(NULL));
 | 
			
		||||
    g_assert(!gbinder_writer_malloc(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_writer_malloc0(NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_writer_memdup(&writer, NULL, 0));
 | 
			
		||||
    g_assert(!gbinder_writer_memdup(NULL, &writer, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * cleanup
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cleanup_fn(
 | 
			
		||||
    gpointer ptr)
 | 
			
		||||
{
 | 
			
		||||
    (*((int*)ptr))++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_cleanup(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    int cleanup_count = 0;
 | 
			
		||||
    int value = 42;
 | 
			
		||||
    int* zero;
 | 
			
		||||
    int* copy;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    zero = gbinder_writer_new0(&writer, int);
 | 
			
		||||
    copy = gbinder_writer_memdup(&writer, &value, sizeof(value));
 | 
			
		||||
    g_assert(*zero == 0);
 | 
			
		||||
    g_assert(*copy == value);
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count);
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    g_assert(cleanup_count == 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -117,6 +165,19 @@ test_int32(
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(value));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, &value, data->bytes->len));
 | 
			
		||||
 | 
			
		||||
    const guint32 value2 = 2345678;
 | 
			
		||||
    gbinder_writer_overwrite_int32(&writer, 0, value2);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(value2));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, &value2, data->bytes->len));
 | 
			
		||||
    
 | 
			
		||||
    // test overlap over the end of the buffer
 | 
			
		||||
    gbinder_writer_overwrite_int32(&writer, 2, value2);
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(value2));
 | 
			
		||||
    
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -202,9 +263,9 @@ test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char encoded[] = {
 | 
			
		||||
        0x00, 0xff, 0xff, 0xff,
 | 
			
		||||
        0x01, 0xff, 0xff, 0xff,
 | 
			
		||||
        0x01, 0xff, 0xff, 0xff
 | 
			
		||||
        0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
@@ -340,6 +401,75 @@ test_string16(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * utf16
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_utf16_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const gunichar2* in;
 | 
			
		||||
    gssize in_len;
 | 
			
		||||
    const guint8* out;
 | 
			
		||||
    gssize out_len;
 | 
			
		||||
} TestUtf16Data;
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_null[] = {
 | 
			
		||||
    TEST_INT32_BYTES(-1)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_empty[] = {
 | 
			
		||||
    TEST_INT32_BYTES(0),
 | 
			
		||||
    0x00, 0x00, 0xff, 0xff
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_x[] = {
 | 
			
		||||
    TEST_INT32_BYTES(1),
 | 
			
		||||
    TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_xy[] = {
 | 
			
		||||
    TEST_INT32_BYTES(2),
 | 
			
		||||
    TEST_INT16_BYTES('x'), TEST_INT16_BYTES('y'),
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const gunichar2 utf16_tests_input_empty[] = { 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_x[] = { 'x', 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_xy[] = { 'x', 'y', 0 };
 | 
			
		||||
 | 
			
		||||
static const TestUtf16Data test_utf16_tests[] = {
 | 
			
		||||
    { "null", NULL, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_null) },
 | 
			
		||||
    { "empty", utf16_tests_input_empty, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_empty) },
 | 
			
		||||
    { "1", utf16_tests_input_x, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "2", utf16_tests_input_xy, 1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "3", utf16_tests_input_xy, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_xy) }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_utf16(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestUtf16Data* test = test_data;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_string16_utf16(&writer, test->in, test->in_len);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == test->out_len);
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, test->out, test->out_len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_vec
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -364,15 +494,15 @@ static guint test_hidl_vec_offsets_64[] =
 | 
			
		||||
 | 
			
		||||
static const TestHidlVecData test_hidl_vec_tests[] = {
 | 
			
		||||
    { "32/null", &gbinder_io_32, NULL, 0, 0,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(HidlVec) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "32/2x1", &gbinder_io_32, "xy", 2, 1,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_32),
 | 
			
		||||
      sizeof(HidlVec) + 8 /* vec data aligned at 8 bytes boundary */ },
 | 
			
		||||
      sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ },
 | 
			
		||||
    { "64/null", &gbinder_io_64, NULL, 0, 0,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(HidlVec) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "64/2x2", &gbinder_io_64, "xxyy", 2, 2,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_64),
 | 
			
		||||
      sizeof(HidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }
 | 
			
		||||
      sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -423,15 +553,17 @@ static guint test_hidl_string_offsets_64[] =
 | 
			
		||||
 | 
			
		||||
static const TestHidlStringData test_hidl_string_tests[] = {
 | 
			
		||||
    { "32/null", &gbinder_io_32, NULL,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0), sizeof(HidlString) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0),
 | 
			
		||||
      sizeof(GBinderHidlString) },
 | 
			
		||||
    { "32/xxx", &gbinder_io_32, "xxx",
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
 | 
			
		||||
      sizeof(HidlString) + 8 /* string data aligned at 8 bytes boundary */ },
 | 
			
		||||
      sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ },
 | 
			
		||||
    { "64/null", &gbinder_io_64, NULL,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0), sizeof(HidlString) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0),
 | 
			
		||||
      sizeof(GBinderHidlString) },
 | 
			
		||||
    { "64/xxxxxxx", &gbinder_io_64, "xxxxxxx",
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
 | 
			
		||||
      sizeof(HidlString) + 8 /* string data aligned at 8 bytes boundary */ }
 | 
			
		||||
      sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -479,8 +611,9 @@ test_hidl_string2(
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(offsets->data[2] == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    /* 2 HidlStrings + "foo" aligned at 8 bytes boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == 2*sizeof(HidlString)+8);
 | 
			
		||||
    /* 2 GBinderHidlStrings + "foo" aligned at 8 bytes boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) ==
 | 
			
		||||
        (2 * sizeof(GBinderHidlString) + 8));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
@@ -511,18 +644,18 @@ static guint test_hidl_string_vec_offsets_1_64[] =
 | 
			
		||||
static const TestHidlStringVecData test_hidl_string_vec_tests[] = {
 | 
			
		||||
    { "32/null", &gbinder_io_32, NULL, -1,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_empty),
 | 
			
		||||
      sizeof(HidlVec) },
 | 
			
		||||
      sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "32/1", &gbinder_io_32,
 | 
			
		||||
      (const char**)TEST_ARRAY_AND_COUNT(test_hidl_string_vec_data_1),
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_1_32),
 | 
			
		||||
      sizeof(HidlVec) + sizeof(HidlString) + 8 },
 | 
			
		||||
      sizeof(GBinderHidlVec) + sizeof(GBinderHidlString) + 8 },
 | 
			
		||||
    { "64/null", &gbinder_io_64, NULL, -1,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_empty),
 | 
			
		||||
      sizeof(HidlVec) },
 | 
			
		||||
      sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "64/1", &gbinder_io_64,
 | 
			
		||||
      (const char**)TEST_ARRAY_AND_COUNT(test_hidl_string_vec_data_1),
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_1_64),
 | 
			
		||||
      sizeof(HidlVec) + sizeof(HidlString) + 8 },
 | 
			
		||||
      sizeof(GBinderHidlVec) + sizeof(GBinderHidlString) + 8 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -631,6 +764,76 @@ test_parent(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * fd
 | 
			
		||||
 * fd_invalid
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd2(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GUtilIntArray* offsets;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_fd(&writer, fd);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_fd2(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd_invalid(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    test_fd2(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * fd_close_error
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd_close_error(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const GBinderIo* io = &gbinder_io_32;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    int fd = -1;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_fd(&writer, STDOUT_FILENO);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32);
 | 
			
		||||
 | 
			
		||||
    /* Fetch duplicated fd and close it. That makes the second close
 | 
			
		||||
     * done by gbinder_writer_data_close_fd() fail. */
 | 
			
		||||
    g_assert(io->decode_fd_object(data->bytes->data, data->bytes->len, &fd));
 | 
			
		||||
    g_assert(close(fd) == 0);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * local_object
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -683,46 +886,128 @@ test_remote_object(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * byte_array
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_byte_array(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req;
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    const char in_data[] = "abcd1234";
 | 
			
		||||
    gint32 in_len = sizeof(in_data) - 1;
 | 
			
		||||
    gint32 null_len = -1;
 | 
			
		||||
 | 
			
		||||
    /* test for NULL byte array with non-zero len */
 | 
			
		||||
    req = gbinder_local_request_new(&gbinder_io_64, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_byte_array(&writer, NULL, 42);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(gint32));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, &null_len, data->bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    /* test for valid array with zero len */
 | 
			
		||||
    req = gbinder_local_request_new(&gbinder_io_64, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_byte_array(&writer, in_data, 0);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(gint32));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, &null_len, data->bytes->len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 | 
			
		||||
    /* test for valid array with correct len */
 | 
			
		||||
    req = gbinder_local_request_new(&gbinder_io_64, NULL);
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_byte_array(&writer, in_data, in_len);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == sizeof(in_len) + in_len);
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, &in_len, sizeof(in_len)));
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data + sizeof(in_len), in_data, in_len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * bytes_written
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_bytes_written(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const guint32 value = 1234567;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    g_assert(gbinder_writer_bytes_written(&writer) == 0);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, value);
 | 
			
		||||
    g_assert(gbinder_writer_bytes_written(&writer) == sizeof(value));
 | 
			
		||||
    
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/writer/"
 | 
			
		||||
#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_PREFIX "null", test_null);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int32", test_int32);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int64", test_int64);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "float", test_float);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "double", test_double);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bool", test_bool);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bytes", test_bytes);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "string8", test_string8);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("cleanup"), test_cleanup);
 | 
			
		||||
    g_test_add_func(TEST_("int32"), test_int32);
 | 
			
		||||
    g_test_add_func(TEST_("int64"), test_int64);
 | 
			
		||||
    g_test_add_func(TEST_("float"), test_float);
 | 
			
		||||
    g_test_add_func(TEST_("double"), test_double);
 | 
			
		||||
    g_test_add_func(TEST_("bool"), test_bool);
 | 
			
		||||
    g_test_add_func(TEST_("bytes"), test_bytes);
 | 
			
		||||
    g_test_add_func(TEST_("string8"), test_string8);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) {
 | 
			
		||||
        const TestString16Data* test = test_string16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "string16/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("string16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_string16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_utf16_tests); i++) {
 | 
			
		||||
        const TestUtf16Data* test = test_utf16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_("utf16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_utf16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) {
 | 
			
		||||
        const TestHidlVecData* test = test_hidl_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_vec/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_vec/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_vec);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "hidl_string/2strings", test_hidl_string2);
 | 
			
		||||
    g_test_add_func(TEST_("hidl_string/2strings"), test_hidl_string2);
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_tests); i++) {
 | 
			
		||||
        const TestHidlStringData* test = test_hidl_string_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_string/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
@@ -730,17 +1015,21 @@ int main(int argc, char* argv[])
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_vec_tests); i++) {
 | 
			
		||||
        const TestHidlStringVecData* test = test_hidl_string_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_string_vec/",
 | 
			
		||||
            test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string_vec/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string_vec);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "buffer", test_buffer);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "parent", test_parent);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "local_object", test_local_object);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "remote_object", test_remote_object);
 | 
			
		||||
    g_test_add_func(TEST_("buffer"), test_buffer);
 | 
			
		||||
    g_test_add_func(TEST_("parent"), test_parent);
 | 
			
		||||
    g_test_add_func(TEST_("fd"), test_fd);
 | 
			
		||||
    g_test_add_func(TEST_("fd_invalid"), test_fd_invalid);
 | 
			
		||||
    g_test_add_func(TEST_("fd_close_error"), test_fd_close_error);
 | 
			
		||||
    g_test_add_func(TEST_("local_object"), test_local_object);
 | 
			
		||||
    g_test_add_func(TEST_("remote_object"), test_remote_object);
 | 
			
		||||
    g_test_add_func(TEST_("byte_array"), test_byte_array);
 | 
			
		||||
    g_test_add_func(TEST_("bytes_written"), test_bytes_written);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user