Compare commits

...

74 Commits

Author SHA1 Message Date
Slava Monich
488fbc5b63 Version 1.0.20 2018-12-17 16:07:50 +02:00
Slava Monich
17f511d7a3 Merge pull request #22 from monich/block
Add API to block incoming requests
2018-12-17 16:04:49 +02:00
Slava Monich
63e633c0ec [test] Added option to test asynchronous completion 2018-12-17 15:46:28 +02:00
Slava Monich
f46448c236 [gbinder] Added API to block incoming requests. JB#43529
GBinderRemoteRequest can be marked as blocked by calling
gbinder_remote_request_block() and later completed with
gbinder_remote_request_complete()
2018-12-17 15:46:19 +02:00
Slava Monich
43023be32d [test] Fixed typo 2018-12-17 00:36:21 +02:00
Slava Monich
4811d51c5d Version 1.0.19 2018-12-14 16:40:31 +02:00
Slava Monich
821dabca3d Merge pull request #21 from monich/cleanup
GBinderWriter allocators and cleanup
2018-12-14 16:01:31 +02:00
Slava Monich
587f4ebb50 [gbinder] GBinderWriter allocators and cleanup. JB#42956
Memory allocated by these allocators will be deallocated together
with the rest of the data.
2018-12-13 15:05:44 +02:00
Slava Monich
0c0b25fcd1 Version 1.0.18 2018-12-10 13:21:53 +02:00
Slava Monich
bdf07d04d4 Merge pull request #20 from monich/fd
Support for file descriptors
2018-12-10 12:56:48 +02:00
Slava Monich
6936675eb9 [gbinder] Added binder-dump test. JB#42956
Similar to Android's dumpsys
2018-12-08 16:35:59 +02:00
Slava Monich
c39bf4b802 [gbinder] Support for file descriptors. JB#42956
gbinder_writer_append_fd() duplicates the descriptor and appends
the duplicate to the parcel. The caller may immediately close its
descriptor.

gbinder_reader_read_fd() fetches the descriptor from the parcel
without duplicating it. The descrriptor will be closed as soon as
the library has finished processing the request. Returns -1 on error.

gbinder_reader_read_dup_fd() fetches descriptor from the parcel
and dups it. The caller is responsible for closing the returned
descriptor. Returns -1 on failure.
2018-12-08 16:27:45 +02:00
Slava Monich
5a43b1b091 [gbinder] Allow GBinderClient without RPC header. JB#42956
Binder transactions don't necessarily have RPC header, for example
GBINDER_DUMP_TRANSACTION and such.
2018-12-08 13:40:44 +02:00
Slava Monich
3e039e033c [gbinder] Close descriptors associated with transactions. Fixes JB#44099
Those are coming as objects of type BINDER_TYPE_FD and the
receiving side has to close them.
2018-12-08 03:12:22 +02:00
Slava Monich
6a2af83ea3 [gbinder] Removed unused GBinderLocalRequest field 2018-12-07 17:09:37 +02:00
Slava Monich
5a12df240b [test] Fail unexpected binder-service transactions 2018-12-07 17:09:04 +02:00
Slava Monich
2ea7d91fc7 [build] Make libgbinder.pc depend on Makefile
It's Makefile which contains the version number which has to match
the one in libgbinder.pc
2018-12-07 16:45:56 +02:00
Slava Monich
d9903e7398 Version 1.0.17 2018-12-07 02:54:57 +02:00
Slava Monich
39b69f27ee Merge pull request #19 from monich/utf16
Reading and writing UTF-16 strings without any conversion
2018-12-07 02:52:12 +02:00
Slava Monich
31a1d19b4e [gbinder] Added gbinder_reader_read_nullable_string16_utf16(). JB#43524
This function allows to read UTF-16 strings from Java parcel as UTF-16,
without any conversion.

As a side effect, it also allows parsing strings containing embedded
NULL characters.
2018-12-06 21:50:08 +02:00
Slava Monich
f4a923c3dc [gbinder] Added gbinder_writer_append_string16_utf16(). JB#43524
This function allows to write UTF-16 strings to Java parcel
without converting UTF-16 to UTF-8 and then back to UTF-16.
As a side effect, it also allows writing strings containing
embedded NULL characters.

This function can be used for adding QString contents to the
request more efficiently.
2018-12-06 21:27:51 +02:00
Slava Monich
171ff7d1e4 Version 1.0.16 2018-12-06 19:06:18 +02:00
Slava Monich
5cfbf22b81 Merge pull request #18 from monich/reader_copy
Added gbinder_reader_copy()
2018-12-06 18:38:36 +02:00
Slava Monich
5a35ed5ea1 [gbinder] Added gbinder_reader_copy(). JB#42956
Even though it's just a straight memcpy at the moment.
Also, constified GBinderReader pointer where appropriate.

This allows passing const GBinderReader pointer as a parameter and
a) allow the callee to parse the payload and b) make sure that the
caller's reader doesn't get damaged (well, or at least warn the callee
that it's doing the wrong thing).
2018-12-06 16:35:30 +02:00
Slava Monich
1978359f15 Merge pull request #17 from monich/hidl_types
Added GBinderHidlVec and GBinderHidlString types
2018-12-06 14:55:36 +02:00
Slava Monich
fef6543f1a [gbinder] Added GBinderHidlVec and GBinderHidlString types. JB#42956
These basic HIDL types are often used as a part of a larger structure.
2018-12-05 20:10:36 +02:00
Slava Monich
b1a9200803 Version 1.0.15 2018-12-05 12:14:45 +02:00
Slava Monich
fada30868a Merge pull request #16 from monich/poll
Implement service polling for old servicemanager
2018-12-05 11:57:29 +02:00
Slava Monich
8414e9485f [unit] Added servicepoll test 2018-12-04 23:48:25 +02:00
Slava Monich
f6d8d485bc [gbinder] Implemented service polling. JB#43524
Old servicemanager protocol didn't implement registration
notifications. Those have to be implemented with polling.
Note that polling will be active as long as at least one
listener is registered, i.e. it's important to unregister
the listener when you don't need it.

The polling interval is 2 seconds.

It doesn't affect hwservicemanager implementation which relies
on notifications, no polling there.
2018-12-04 23:43:49 +02:00
Slava Monich
6ea0d6c631 [unit] Added test for HIDL_PING_TRANSACTION 2018-12-04 15:07:27 +02:00
Slava Monich
31c6c05c1e [unit] Added unit_servicemanager test 2018-12-04 13:31:18 +02:00
Slava Monich
d855d695db Version 1.0.14 2018-11-27 17:21:33 +02:00
Slava Monich
922cc82029 Merge pull request #15 from monich/bool
Change bool padding from 0xff to 0x00
2018-11-27 17:15:57 +02:00
Slava Monich
80498378f2 [gbinder] Change bool padding from 0xff to 0x00. JB#42956
That should make it compatible with both java and hidl variants of
encoding (Android's libbinder vs libhwbinder)
2018-11-27 16:59:11 +02:00
Slava Monich
51856865df Version 1.0.13 2018-11-17 01:58:33 +02:00
Slava Monich
d26dcca37e [gbinder] Make sure looper is started
... before gbinder_ipc_looper_new() returns.
2018-11-17 01:49:50 +02:00
Slava Monich
5ac02fcb2e Merge pull request #14 from monich/registration
Support for service registration notifications
2018-11-17 01:47:28 +02:00
Slava Monich
110cd65779 [gbinder] Support for service registration notifications. JB#43815
android.hidl.manager@1.0::IServiceManager provides registration
notifications via android.hidl.manager@1.0::IServiceNotification
callback.

Sailfish OS services that talk to Android services via binder should
use this mechanism to wait for their Android counterpart at startup.
2018-11-17 01:41:02 +02:00
Slava Monich
67e665b619 Version 1.0.12 2018-10-31 17:07:45 +03:00
Slava Monich
d1c431c370 Acknowledge contributions 2018-10-31 17:03:44 +03:00
Slava Monich
2167a82c73 Housekeeping 2018-10-31 17:01:00 +03:00
Slava Monich
d113d3bf4a Merge pull request #12 from jusa/byte_array
Add byte array reader and writer.
2018-10-31 16:40:33 +03:00
Juho Hämäläinen
b2206adae5 [gbinder] Add byte array reader and writer. JB#43536
New functions gbinder_reader_read_byte_array() and
gbinder_writer_append_byte_array()
2018-10-30 07:28:19 +02:00
Slava Monich
8075cce1b1 Version 1.0.11 2018-10-24 18:59:30 +03:00
Slava Monich
29e4c79f75 Merge pull request #11 from krnlyng/jb43524
Make sure NULL objects are passed as BINDER_TYPE_WEAK_HANDLE
2018-10-24 18:53:58 +03:00
Franz-Josef Haider
3b299d3345 [local_object] Make sure NULL objects are passed as BINDER_TYPE_WEAK_HANDLE. Contributes to JB#43524
See also: https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/Parcel.cpp#264
2018-10-24 15:41:27 +00:00
Slava Monich
7421fff380 Updated README 2018-10-11 12:04:04 +03:00
Slava Monich
694aad637b Version 1.0.10 2018-10-10 14:46:40 +03:00
Slava Monich
c8c7222e06 [unit] Plugged memory leak in unit_reader. JB#42956 2018-10-10 14:44:25 +03:00
Slava Monich
9c6e31ef41 Merge pull request #10 from monich/deps
Fix dependencies for unit tests
2018-10-10 14:39:17 +03:00
Slava Monich
6a8d5c0c6e [unit] Fixed dependencies for unit tests. JB#42691 2018-10-10 14:38:38 +03:00
Slava Monich
96ca10396b Version 1.0.9 2018-10-08 11:43:53 +03:00
Slava Monich
61cef824e8 Merge pull request #9 from monich/read_hidl_struct
Add gbinder_reader_read_hidl_struct() and other macros
2018-10-08 11:20:13 +03:00
Slava Monich
972517d32d Merge pull request #8 from monich/strong_refs
Respect strong refs to GBinderLocalObject
2018-10-08 11:17:00 +03:00
Slava Monich
217a03642a [unit] Added test for new gbinder_reader_read_hidl_struct() 2018-10-08 11:13:00 +03:00
Slava Monich
f14783b8cf [gbinder] Add gbinder_reader_read_hidl_struct() macro. JB#42956
As well as two macros for reading arrays:

   gbinder_reader_read_hidl_type_vec() - vec<type>
   gbinder_reader_read_hidl_byte_vec() - vec<uint8_t>

These convenient for pulling structures and arrays from hwbinder
requests and responses.

Note that data are not being copied, all these macros return pointers
to the memory buffer passed to us by the kernel. Such pointers remain
valid as long the associated remote request/response is alive.
2018-10-06 18:07:39 +03:00
Slava Monich
4f75c6e37b [gbinder] Respect strong refs to GBinderLocalObject. JB#42956
This lets kernel to keep the object alive for a long time, after the
creator of the object drops its last reference.

That makes it even more important very important that creator drops
its last reference by calling gbinder_local_object_drop() rather than
gbinder_local_object_unref() to make sure that the handler function
doesn't get called anymore.
2018-10-06 11:39:02 +03:00
Slava Monich
4eb3b66a0e Version 1.0.8 2018-09-25 01:09:53 +03:00
Slava Monich
6b74d5faed Merge pull request #7 from monich/hidl_vec
Add gbinder_writer_append_hidl_vec() and gbinder_reader_read_hidl_vec()
2018-09-25 01:05:39 +03:00
Slava Monich
10d72ec42c [unit] Added test for gbinder_reader_read_hidl_vec() 2018-09-24 03:33:42 +03:00
Slava Monich
9eae4ef819 [gbinder] Added gbinder_reader_read_hidl_vec(). JB#42956
This provides a generic way to read any array of any primitive
types or structures. Note that it doesn't copy the data and
directly returns the pointer provided by the kernel.
2018-09-24 03:14:33 +03:00
Slava Monich
b3657e396f [unit] Added test for gbinder_writer_append_hidl_vec() 2018-09-24 00:48:56 +03:00
Slava Monich
a69885d05f [gbinder] Added gbinder_writer_append_hidl_vec(). JB#42956
This provides a generic way to write hidl-style array of any primitive
types or any structures as long as they don't contain pointers.
2018-09-24 00:47:14 +03:00
Slava Monich
306d08112b Version 1.0.7 2018-09-23 22:49:16 +03:00
Slava Monich
b489becf51 Merge pull request #5 from monich/forward
Add gbinder_remote_request_copy_to_local() and gbinder_remote_reply_copy_to_local()
2018-09-23 22:44:27 +03:00
Slava Monich
8a27829af3 [gbinder] Make sure RPC protocol matches servicemanager type
This makes it easier to use non-standard binder device name.
2018-09-22 12:15:20 +03:00
Slava Monich
d67b73502e [unit] Added test for gbinder_remote_reply_copy_to_local() 2018-09-22 12:15:20 +03:00
Slava Monich
4ecf3ae24d [gbinder] Added gbinder_remote_reply_copy_to_local(). JB#42956 2018-09-22 12:15:20 +03:00
Slava Monich
d0542e759d [unit] Added test for gbinder_remote_request_copy_to_local() 2018-09-22 12:15:20 +03:00
Slava Monich
ffa44ac5b3 [gbinder] Added gbinder_remote_request_copy_to_local(). JB#42956
This function could be useful for proxy services, as an easy way to
forward an incoming request. To complete the picture, we should also
have gbinder_remote_reply_copy_to_local() for forwarding replies.
2018-09-22 12:15:20 +03:00
Slava Monich
6509e5dac8 Version 1.0.6 2018-09-21 21:16:29 +03:00
Slava Monich
6b956c0d3c Merge pull request #6 from monich/ref_count
Fix GBinderServiceManager lifecycle management
2018-09-21 21:14:01 +03:00
Slava Monich
3b10a08bad [gbinder] Fixed GBinderServiceManager lifecycle management. Fixes JB#43018
Dispose callback is invoked with refcount 1
2018-09-21 20:27:40 +03:00
74 changed files with 6748 additions and 827 deletions

View File

@@ -1,2 +1,4 @@
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>

View File

@@ -24,7 +24,7 @@ all: debug release pkgconfig
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_RELEASE = 5
VERSION_RELEASE = 20
# Version for pkg-config
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
@@ -62,6 +62,7 @@ SRC = \
gbinder_remote_reply.c \
gbinder_remote_request.c \
gbinder_rpc_protocol.c \
gbinder_servicepoll.c \
gbinder_writer.c
SRC += \
@@ -247,7 +248,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
View File

@@ -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

103
debian/changelog vendored
View File

@@ -1,3 +1,106 @@
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
* Plugged memory leak in unit_reader
-- Slava Monich <slava.monich@jolla.com> Wed, 10 Oct 2018 14:44:44 +0300
libgbinder (1.0.9) unstable; urgency=low
* Respect strong refs to GBinderLocalObject
* Added gbinder_reader_read_hidl_struct macro
* Added gbinder_reader_read_hidl_type_vec macro
* Added gbinder_reader_read_hidl_byte_vec macro
-- Slava Monich <slava.monich@jolla.com> Mon, 08 Oct 2018 11:41:33 +0300
libgbinder (1.0.8) unstable; urgency=low
* Added gbinder_writer_append_hidl_vec()
* Added Added gbinder_reader_read_hidl_vec()
-- Slava Monich <slava.monich@jolla.com> Tue, 25 Sep 2018 01:08:54 +0300
libgbinder (1.0.7) unstable; urgency=low
* Added gbinder_remote_request_copy_to_local()
* Added gbinder_remote_reply_copy_to_local()
* Make sure RPC protocol matches servicemanager type
-- Slava Monich <slava.monich@jolla.com> Sun, 23 Sep 2018 22:47:16 +0300
libgbinder (1.0.6) unstable; urgency=low
* Fixed GBinderServiceManager lifecycle management
-- Slava Monich <slava.monich@jolla.com> Fri, 21 Sep 2018 21:15:28 +0300
libgbinder (1.0.5) unstable; urgency=low
* Added double and float support

4
debian/control vendored
View File

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

View File

@@ -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
@@ -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,
@@ -113,6 +122,31 @@ gbinder_reader_read_buffer(
GBinderReader* reader)
G_GNUC_WARN_UNUSED_RESULT;
const void*
gbinder_reader_read_hidl_struct1(
GBinderReader* reader,
gsize size); /* since 1.0.9 */
#define gbinder_reader_read_hidl_struct(reader,type) \
((const type*)gbinder_reader_read_hidl_struct1(reader, sizeof(type)))
const void*
gbinder_reader_read_hidl_vec(
GBinderReader* reader,
gsize* count,
gsize* elemsize);
const void*
gbinder_reader_read_hidl_vec1(
GBinderReader* reader,
gsize* count,
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)))
#define gbinder_reader_read_hidl_byte_vec(reader,count) /* vec<uint8_t> */ \
gbinder_reader_read_hidl_type_vec(reader,guint8,count)
char*
gbinder_reader_read_hidl_string(
GBinderReader* reader)
@@ -140,17 +174,33 @@ gbinder_reader_read_nullable_string16(
GBinderReader* reader,
char** out);
gboolean
gbinder_reader_read_nullable_string16_utf16(
GBinderReader* reader,
gunichar2** out,
gsize* len); /* since 1.0.17 */
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

View File

@@ -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
@@ -50,6 +50,11 @@ gbinder_remote_reply_init_reader(
GBinderRemoteReply* reply,
GBinderReader* reader);
GBinderLocalReply*
gbinder_remote_reply_copy_to_local(
GBinderRemoteReply* reply) /* since 1.0.6 */
G_GNUC_WARN_UNUSED_RESULT;
/* Convenience function to decode replies with just one data item */
gboolean

View File

@@ -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
@@ -62,6 +62,21 @@ uid_t
gbinder_remote_request_sender_euid(
GBinderRemoteRequest* req); /* since 1.0.2 */
GBinderLocalRequest*
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

View File

@@ -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,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);
@@ -130,6 +137,18 @@ gbinder_servicemanager_cancel(
GBinderServiceManager* sm,
gulong id);
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 */
G_END_DECLS
#endif /* GBINDER_SERVICEMANAGER_H */

View File

@@ -72,6 +72,30 @@ 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.

View File

@@ -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
@@ -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,11 @@ gbinder_writer_append_bytes(
const void* data,
gsize size);
void
gbinder_writer_append_fd(
GBinderWriter* writer,
int fd); /* Since 1.0.18 */
guint
gbinder_writer_append_buffer_object_with_parent(
GBinderWriter* writer,
@@ -121,6 +132,13 @@ gbinder_writer_append_buffer_object(
const void* buf,
gsize len);
void
gbinder_writer_append_hidl_vec(
GBinderWriter* writer,
const void* base,
guint count,
guint elemsize); /* since 1.0.8 */
void
gbinder_writer_append_hidl_string(
GBinderWriter* writer,
@@ -142,6 +160,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 */

View File

@@ -1,14 +1,14 @@
Name: libgbinder
Version: 1.0.5
Version: 1.0.20
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.34
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(libglibutil) >= 1.0.29
BuildRequires: pkgconfig(libglibutil) >= 1.0.34
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig

View File

@@ -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,55 +36,62 @@
#include <gutil_macros.h>
typedef struct gbinder_buffer_memory {
struct gbinder_buffer_contents {
gint refcount;
void* buffer;
gsize size;
void** objects;
GBinderDriver* driver;
} GBinderBufferMemory;
};
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);
}
static
GBinderBufferMemory*
gbinder_buffer_memory_ref(
GBinderBufferMemory* self)
GBinderBufferContents*
gbinder_buffer_contents_ref(
GBinderBufferContents* self)
{
if (G_LIKELY(self)) {
GASSERT(self->refcount > 0);
@@ -93,15 +100,14 @@ gbinder_buffer_memory_ref(
return self;
}
static
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);
}
}
}
@@ -113,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;
@@ -133,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);
}
}
@@ -142,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*
@@ -155,10 +163,30 @@ gbinder_buffer_new_with_parent(
gsize size)
{
return gbinder_buffer_alloc(parent ?
gbinder_buffer_memory_ref(gbinder_buffer_cast(parent)->memory) : NULL,
gbinder_buffer_contents_ref(gbinder_buffer_contents(parent)) : NULL,
data, size);
}
gconstpointer
gbinder_buffer_data(
GBinderBuffer* self,
gsize* size)
{
GBinderBufferContents* contents = gbinder_buffer_contents(self);
if (G_LIKELY(contents)) {
if (size) {
*size = contents->size;
}
return contents->buffer;
} else {
if (size) {
*size = 0;
}
return NULL;
}
}
GBinderDriver*
gbinder_buffer_driver(
GBinderBuffer* self)
@@ -166,13 +194,43 @@ 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;
}
const GBinderIo*
gbinder_buffer_io(
GBinderBuffer* buf)
{
GBinderDriver* driver = gbinder_buffer_driver(buf);
return driver ? gbinder_driver_io(driver) : NULL;
}
void**
gbinder_buffer_objects(
GBinderBuffer* self)
{
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;
}
/*
* Local Variables:
* mode: C

View File

@@ -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
@@ -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,30 @@ GBinderDriver*
gbinder_buffer_driver(
GBinderBuffer* buf);
#define gbinder_buffer_io(buf) \
gbinder_driver_io(gbinder_buffer_driver(buf))
GBinderBufferContents*
gbinder_buffer_contents(
GBinderBuffer* buf);
gconstpointer
gbinder_buffer_data(
GBinderBuffer* buf,
gsize* size);
const GBinderIo*
gbinder_buffer_io(
GBinderBuffer* buf);
void**
gbinder_buffer_objects(
GBinderBuffer* buffer);
GBinderBufferContents*
gbinder_buffer_contents_ref(
GBinderBufferContents* contents);
void
gbinder_buffer_contents_unref(
GBinderBufferContents* contents);
#endif /* GBINDER_BUFFER_PRIVATE_H */

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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
@@ -31,6 +31,8 @@
*/
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_servicepoll.h"
#include "gbinder_log.h"
#include <gbinder_client.h>
@@ -42,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,
@@ -65,7 +85,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
@@ -150,11 +239,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
@@ -165,10 +304,16 @@ gbinder_defaultservicemanager_class_init(
klass->handle = DEFAULTSERVICEMANAGER_HANDLE;
klass->iface = DEFAULTSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_BINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = gbinder_defaultservicemanager_list;
klass->get_service = gbinder_defaultservicemanager_get_service;
klass->add_service = gbinder_defaultservicemanager_add_service;
klass->check_name = gbinder_defaultservicemanager_check_name;
/* normalize_name is not needed */
klass->watch = gbinder_defaultservicemanager_watch;
klass->unwatch = gbinder_defaultservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = gbinder_defaultservicemanager_finalize;
}
/*

View File

@@ -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
@@ -439,11 +439,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);
}
@@ -637,10 +636,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);
}
@@ -672,7 +670,8 @@ gbinder_driver_txstatus(
GBinderDriver*
gbinder_driver_new(
const char* dev)
const char* dev,
const GBinderRpcProtocol* protocol)
{
const int fd = gbinder_system_open(dev, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
@@ -711,8 +710,10 @@ gbinder_driver_new(
GERR("%s failed to set max threads (%u): %s", dev,
max_threads, strerror(errno));
}
/* Choose the protocol based on the device name */
self->protocol = gbinder_rpc_protocol_for_device(dev);
/* Choose the protocol based on the device name
* if none is explicitely specified */
self->protocol = protocol ? protocol :
gbinder_rpc_protocol_for_device(dev);
return self;
} else {
GERR("%s failed to mmap: %s", dev, strerror(errno));
@@ -860,6 +861,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,

View File

@@ -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,7 +39,8 @@ struct pollfd;
GBinderDriver*
gbinder_driver_new(
const char* dev);
const char* dev,
const GBinderRpcProtocol* protocol);
GBinderDriver*
gbinder_driver_ref(
@@ -96,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,

View File

@@ -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
@@ -31,23 +31,40 @@
*/
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#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,
@@ -59,9 +76,76 @@ enum gbinder_hwservicemanager_calls {
REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
};
enum gbinder_hwservicemanager_notifications {
ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
};
/* As a special case, ServiceManager's handle is zero */
#define HWSERVICEMANAGER_HANDLE (0)
#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(
@@ -74,7 +158,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
@@ -110,6 +194,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);
@@ -169,11 +254,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
@@ -184,10 +381,16 @@ gbinder_hwservicemanager_class_init(
klass->handle = HWSERVICEMANAGER_HANDLE;
klass->iface = HWSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
klass->list = gbinder_hwservicemanager_list;
klass->get_service = gbinder_hwservicemanager_get_service;
klass->add_service = gbinder_hwservicemanager_add_service;
klass->check_name = gbinder_hwservicemanager_check_name;
klass->normalize_name = gbinder_hwservicemanager_normalize_name;
klass->watch = gbinder_hwservicemanager_watch;
klass->unwatch = gbinder_hwservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = gbinder_hwservicemanager_finalize;
}
/*

View File

@@ -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
@@ -90,6 +90,21 @@ GBINDER_IO_FN(write_read)(
return ret;
}
/* Returns size of the object's extra data */
static
gsize
GBINDER_IO_FN(object_data_size)(
const void* obj)
{
const struct binder_buffer_object* buf = obj;
if (buf && buf->hdr.type == BINDER_TYPE_PTR) {
return buf->length;
} else {
return 0;
}
}
/* Writes pointer to the buffer */
static
guint
@@ -113,7 +128,7 @@ GBINDER_IO_FN(encode_local_object)(
struct flat_binder_object* dest = out;
memset(dest, 0, sizeof(*dest));
dest->hdr.type = BINDER_TYPE_BINDER;
dest->hdr.type = obj ? BINDER_TYPE_BINDER : BINDER_TYPE_WEAK_HANDLE;
dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
dest->binder = (uintptr_t)obj;
return sizeof(*dest);
@@ -138,6 +153,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
@@ -396,6 +426,28 @@ GBINDER_IO_FN(decode_buffer_object)(
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;
}
const GBinderIo GBINDER_IO_PREFIX = {
.version = BINDER_CURRENT_PROTOCOL_VERSION,
.pointer_size = GBINDER_POINTER_SIZE,
@@ -445,10 +497,13 @@ const GBinderIo GBINDER_IO_PREFIX = {
.failed_reply = BR_FAILED_REPLY
},
.object_data_size = GBINDER_IO_FN(object_data_size),
/* Encoders */
.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),
@@ -461,6 +516,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)

View File

@@ -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
@@ -118,6 +118,9 @@ struct gbinder_io {
guint failed_reply;
} br;
/* Size of the object's extra data */
gsize (*object_data_size)(const void* obj);
/* Writes pointer to the buffer. The destination buffer must have
* at least GBINDER_IO_MAX_POINTER_SIZE bytes available. The
* actual size is returned. */
@@ -128,6 +131,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,6 +168,7 @@ struct gbinder_io {
GBinderObjectRegistry* reg, GBinderRemoteObject** obj);
guint (*decode_buffer_object)(GBinderBuffer* buf, gsize offset,
GBinderBuffer** out);
guint (*decode_fd_object)(const void* data, gsize size, int* fd);
/* ioctl wrappers */
int (*write_read)(int fd, GBinderIoBuf* write, GBinderIoBuf* read);

View File

@@ -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
@@ -68,9 +68,9 @@ struct gbinder_ipc_priv {
GMutex local_objects_mutex;
GHashTable* local_objects;
/* We may need more loopers... But let's start with just one */
GMutex looper_mutex;
GBinderIpcLooper* looper;
GBinderIpcLooper* primary_loopers;
GBinderIpcLooper* blocked_loopers;
};
typedef GObjectClass GBinderIpcClass;
@@ -88,6 +88,7 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
#define GBINDER_IPC_MAX_TX_THREADS (15)
#define GBINDER_IPC_MAX_LOOPERS (15)
#define GBINDER_IPC_LOOPER_START_TIMEOUT_SEC (2)
/*
* When looper receives the transaction:
@@ -99,7 +100,7 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
* When the main thread receives GBinderIpcLooperTx:
*
* 1. Lets the object to process it and produce the response (GBinderOutput).
* 2. Writes one byte to the sending end of the tx pipe.
* 2. Writes one byte (TX_DONE) to the sending end of the tx pipe.
* 3. Unreferences GBinderIpcLooperTx
*
* When tx pipe wakes up the looper:
@@ -110,11 +111,26 @@ static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER;
* Note that GBinderIpcLooperTx can be deallocated on either looper or
* main thread, depending on whether looper gives up on the transaction
* before it gets processed.
*
* When transaction is blocked by gbinder_remote_request_block() call, it
* gets slightly more complicated. Then the main thread writes TX_BLOCKED
* to the pipe (rather than TX_DONE) and then looper thread spawn another
* looper and keeps waiting for TX_DONE.
*/
#define TX_DONE (0x2a)
#define TX_BLOCKED (0x3b)
typedef struct gbinder_ipc_looper_tx {
typedef enum gbinder_ipc_looper_tx_state {
GBINDER_IPC_LOOPER_TX_SCHEDULED,
GBINDER_IPC_LOOPER_TX_PROCESSING,
GBINDER_IPC_LOOPER_TX_PROCESSED,
GBINDER_IPC_LOOPER_TX_BLOCKING,
GBINDER_IPC_LOOPER_TX_BLOCKED,
GBINDER_IPC_LOOPER_TX_COMPLETE
} GBINDER_IPC_LOOPER_TX_STATE;
struct gbinder_ipc_looper_tx {
/* Reference count */
gint refcount;
/* These are filled by the looper: */
@@ -124,16 +140,23 @@ typedef struct gbinder_ipc_looper_tx {
GBinderLocalObject* obj;
GBinderRemoteRequest* req;
/* And these by the main thread processing the transaction: */
GBINDER_IPC_LOOPER_TX_STATE state;
GBinderLocalReply* reply;
int status;
} GBinderIpcLooperTx;
} /* GBinderIpcLooperTx */;
struct gbinder_ipc_looper {
gint refcount;
GBinderIpcLooper* next;
char* name;
GBinderHandler handler;
GBinderDriver* driver;
GBinderIpc* ipc; /* Not a reference! */
GThread* thread;
GMutex mutex;
GCond start_cond;
gint exit;
gint started;
int pipefd[2];
int txfd[2];
};
@@ -174,6 +197,11 @@ typedef struct gbinder_ipc_tx_custom {
GBINDER_INLINE_FUNC const char* gbinder_ipc_name(GBinderIpc* self)
{ return gbinder_driver_dev(self->driver); }
static
GBinderIpcLooper*
gbinder_ipc_looper_new(
GBinderIpc* ipc);
/*==========================================================================*
* GBinderIpcLooperTx
*==========================================================================*/
@@ -241,6 +269,91 @@ gbinder_ipc_looper_tx_unref(
return dropped;
}
/*==========================================================================*
* State machine of transaction handling. All this is happening on the event
* thread and therefore doesn't need to be synchronized.
*
* SCHEDULED
* =========
* |
* PROCESSING
* ==========
* |
* --------------------- handler is called ---------------------------------
* |
* +---------------- request doesn't need to be blocked ----------+
* | |
* gbinder_remote_request_block() |
* | |
* BLOCKING -- gbinder_remote_request_complete() --> PROCESSED |
* ======== ========= |
* | | |
* --------------------- handler returns -----------------------------------
* | | |
* BLOCKED COMPLETE <-------+
* ======= ========
* ^
* ... |
* gbinder_remote_request_complete() is called later ----+
*==========================================================================*/
void
gbinder_remote_request_block(
GBinderRemoteRequest* req) /* Since 1.0.20 */
{
if (G_LIKELY(req)) {
GBinderIpcLooperTx* tx = req->tx;
GASSERT(tx);
if (G_LIKELY(tx)) {
GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING);
if (tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING) {
tx->state = GBINDER_IPC_LOOPER_TX_BLOCKING;
}
}
}
}
void
gbinder_remote_request_complete(
GBinderRemoteRequest* req,
GBinderLocalReply* reply,
int status) /* Since 1.0.20 */
{
if (G_LIKELY(req)) {
GBinderIpcLooperTx* tx = req->tx;
GASSERT(tx);
if (G_LIKELY(tx)) {
const guint8 done = TX_DONE;
switch (tx->state) {
case GBINDER_IPC_LOOPER_TX_BLOCKING:
/* Called by the transaction handler */
tx->status = status;
tx->reply = gbinder_local_reply_ref(reply);
tx->state = GBINDER_IPC_LOOPER_TX_PROCESSED;
break;
case GBINDER_IPC_LOOPER_TX_BLOCKED:
/* Really asynchronous completion */
tx->status = status;
tx->reply = gbinder_local_reply_ref(reply);
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
/* Wake up the looper */
(void)write(tx->pipefd[1], &done, sizeof(done));
break;
default:
GWARN("Unexpected state %d in request completion", tx->state);
break;
}
/* Clear the transaction reference */
gbinder_ipc_looper_tx_unref(tx, FALSE);
req->tx = NULL;
}
}
}
/*==========================================================================*
* GBinderIpcLooper
*==========================================================================*/
@@ -251,11 +364,70 @@ gbinder_ipc_looper_tx_handle(
gpointer data)
{
GBinderIpcLooperTx* tx = data;
guint8 done = TX_DONE;
GBinderRemoteRequest* req = tx->req;
GBinderLocalReply* reply;
int status = GBINDER_STATUS_OK;
guint8 done;
/*
* Transaction reference for gbinder_remote_request_block()
* and gbinder_remote_request_complete().
*/
req->tx = gbinder_ipc_looper_tx_ref(tx);
/* See state machine */
GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_SCHEDULED);
tx->state = GBINDER_IPC_LOOPER_TX_PROCESSING;
/* Actually handle the transaction */
tx->reply = gbinder_local_object_handle_transaction(tx->obj, tx->req,
tx->code, tx->flags, &tx->status);
reply = gbinder_local_object_handle_transaction(tx->obj, req,
tx->code, tx->flags, &status);
/* Handle all possible return states */
switch (tx->state) {
case GBINDER_IPC_LOOPER_TX_PROCESSING:
/* Result was returned by the handler */
tx->reply = reply;
tx->status = status;
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
reply = NULL;
break;
case GBINDER_IPC_LOOPER_TX_PROCESSED:
/* Result has been provided to gbinder_remote_request_complete() */
tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE;
break;
case GBINDER_IPC_LOOPER_TX_BLOCKING:
/* Result will be provided to gbinder_remote_request_complete() */
tx->state = GBINDER_IPC_LOOPER_TX_BLOCKED;
break;
default:
break;
}
/* In case handler returns a reply which it wasn't expected to return */
GASSERT(!reply);
gbinder_local_reply_unref(reply);
/* Drop the transaction reference unless blocked */
if (tx->state == GBINDER_IPC_LOOPER_TX_BLOCKED) {
done = TX_BLOCKED;
/*
* From this point on, it's GBinderRemoteRequest who's holding
* reference to GBinderIpcLooperTx, not the other way around and
* not both ways. Even if gbinder_remote_request_complete() never
* gets called, transaction will still be completed when the last
* reference to GBinderRemoteRequest goes away. And if request
* never gets deallocated... oh well.
*/
gbinder_remote_request_unref(tx->req);
tx->req = NULL;
} else {
done = TX_DONE;
if (req->tx) {
gbinder_ipc_looper_tx_unref(req->tx, FALSE);
req->tx = NULL;
}
}
/* And wake up the looper */
(void)write(tx->pipefd[1], &done, sizeof(done));
@@ -270,6 +442,52 @@ gbinder_ipc_looper_tx_done(
gbinder_ipc_looper_tx_unref(data, FALSE);
}
static
gboolean
gbinder_ipc_looper_remove_from_list(
GBinderIpcLooper* looper,
GBinderIpcLooper** list)
{
/* Caller holds looper_mutex */
if (*list) {
if ((*list) == looper) {
(*list) = looper->next;
looper->next = NULL;
return TRUE;
} else {
GBinderIpcLooper* prev = (*list);
while (prev->next) {
if (prev->next == looper) {
prev->next = looper->next;
looper->next = NULL;
return TRUE;
}
prev = prev->next;
}
}
}
return FALSE;
}
static
gboolean
gbinder_ipc_looper_remove_primary(
GBinderIpcLooper* looper)
{
return gbinder_ipc_looper_remove_from_list(looper,
&looper->ipc->priv->primary_loopers);
}
static
gboolean
gbinder_ipc_looper_remove_blocked(
GBinderIpcLooper* looper)
{
return gbinder_ipc_looper_remove_from_list(looper,
&looper->ipc->priv->blocked_loopers);
}
static
GBinderLocalReply*
gbinder_ipc_looper_transact(
@@ -314,11 +532,65 @@ gbinder_ipc_looper_transact(
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
/* Normal completion */
if (done == TX_BLOCKED) {
/*
* We are going to block this looper for potentially
* significant period of time. Start new looper to
* accept normal incoming requests and terminate this
* one when we are done with this transaction.
*
* For the duration of the transaction, this looper is
* moved to the blocked_loopers list.
*/
GBinderIpcPriv* priv = looper->ipc->priv;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (gbinder_ipc_looper_remove_primary(looper)) {
GBinderIpcLooper* new_looper;
GDEBUG("Primary looper %s is blocked", looper->name);
looper->next = priv->blocked_loopers;
priv->blocked_loopers = looper;
/* Looper will exit once transaction completes */
g_atomic_int_set(&looper->exit, 1);
/* Create new primary looper to replace this one */
new_looper = gbinder_ipc_looper_new(ipc);
if (new_looper) {
new_looper->next = priv->primary_loopers;
priv->primary_loopers = new_looper;
}
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
/* Block until asynchronous transaction gets completed. */
done = 0;
memset(fds, 0, sizeof(fds));
fds[0].fd = looper->pipefd[0];
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
fds[1].fd = tx->pipefd[0];
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
poll(fds, 2, -1);
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
GASSERT(done == TX_DONE);
}
}
}
if (done) {
GASSERT(done == TX_DONE);
reply = gbinder_local_reply_ref(tx->reply);
status = tx->status;
if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) {
/* gbinder_ipc_looper_tx_free() will close those */
/*
* This wasn't the last references meaning that
* gbinder_ipc_looper_tx_free() will close the
* descriptors and we will have to create a new
* pipe for the next transaction.
*/
looper->txfd[0] = looper->txfd[1] = -1;
}
} else {
@@ -344,6 +616,9 @@ gbinder_ipc_looper_free(
close(looper->txfd[1]);
}
gbinder_driver_unref(looper->driver);
g_free(looper->name);
g_cond_clear(&looper->start_cond);
g_mutex_clear(&looper->mutex);
g_slice_free(GBinderIpcLooper, looper);
}
@@ -378,38 +653,45 @@ gbinder_ipc_looper_thread(
if (gbinder_driver_enter_looper(driver)) {
struct pollfd pipefd;
int result;
int res;
GDEBUG("Looper %s running", looper->name);
g_mutex_lock(&looper->mutex);
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
GDEBUG("Looper %s running", gbinder_driver_dev(driver));
memset(&pipefd, 0, sizeof(pipefd));
pipefd.fd = looper->pipefd[0]; /* read end of the pipe */
pipefd.events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
result = gbinder_driver_poll(driver, &pipefd);
while (looper->ipc && ((result & POLLIN) || !result)) {
if (result & POLLIN) {
/* No need to synchronize access to looper->ipc because
res = gbinder_driver_poll(driver, &pipefd);
while (!g_atomic_int_get(&looper->exit) && ((res & POLLIN) || !res)) {
if (res & POLLIN) {
/*
* No need to synchronize access to looper->ipc because
* the other thread would wait until this thread exits
* before setting looper->ipc to NULL */
* before setting looper->ipc to NULL.
*/
GBinderIpc* ipc = gbinder_ipc_ref(looper->ipc);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
/* But that gbinder_driver_read() may unref GBinderIpc */
int ret = gbinder_driver_read(driver, reg, &looper->handler);
/* And this gbinder_ipc_unref() may release the last ref: */
gbinder_ipc_unref(ipc);
/* And at this point looper->ipc may be NULL */
if (ret < 0) {
GDEBUG("Looper %s failed", gbinder_driver_dev(driver));
GDEBUG("Looper %s failed", looper->name);
break;
}
}
if (pipefd.revents) {
/* Any event from this pipe terminates the loop */
GDEBUG("Looper %s is asked to exit",
gbinder_driver_dev(driver));
/* Any event from this pipe terminates the loop */
if (pipefd.revents || g_atomic_int_get(&looper->exit)) {
GDEBUG("Looper %s is requested to exit", looper->name);
break;
}
result = gbinder_driver_poll(driver, &pipefd);
res = gbinder_driver_poll(driver, &pipefd);
}
gbinder_driver_exit_looper(driver);
@@ -421,21 +703,28 @@ gbinder_ipc_looper_thread(
*/
if (looper->ipc) {
GBinderIpcPriv* priv = looper->ipc->priv;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (priv->looper == looper) {
if (gbinder_ipc_looper_remove_blocked(looper) ||
gbinder_ipc_looper_remove_primary(looper)) {
/* Spontaneous exit */
priv->looper = NULL;
GDEBUG("Looper %s exits", gbinder_driver_dev(driver));
GDEBUG("Looper %s exits", looper->name);
gbinder_ipc_looper_unref(looper);
} else {
/* Main thread is shutting it down */
GDEBUG("Looper %s done", gbinder_driver_dev(driver));
GDEBUG("Looper %s done", looper->name);
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
} else {
GDEBUG("Looper %s is abandoned", gbinder_driver_dev(driver));
GDEBUG("Looper %s is abandoned", looper->name);
}
} else {
g_mutex_lock(&looper->mutex);
g_atomic_int_set(&looper->started, TRUE);
g_cond_broadcast(&looper->start_cond);
g_mutex_unlock(&looper->mutex);
}
gbinder_ipc_looper_unref(looper);
@@ -456,18 +745,24 @@ gbinder_ipc_looper_new(
};
GError* error = NULL;
GBinderIpcLooper* looper = g_slice_new0(GBinderIpcLooper);
static gint gbinder_ipc_next_looper_id = 1;
guint id = (guint)g_atomic_int_add(&gbinder_ipc_next_looper_id, 1);
memcpy(looper->pipefd, fd, sizeof(fd));
looper->txfd[0] = looper->txfd[1] = -1;
g_atomic_int_set(&looper->refcount, 1);
g_cond_init(&looper->start_cond);
g_mutex_init(&looper->mutex);
looper->name = g_strdup_printf("%s#%u", gbinder_ipc_name(ipc), id);
looper->handler.f = &handler_functions;
looper->ipc = ipc;
looper->driver = gbinder_driver_ref(ipc->driver);
looper->thread = g_thread_try_new(gbinder_ipc_name(ipc),
looper->thread = g_thread_try_new(looper->name,
gbinder_ipc_looper_thread, looper, &error);
if (looper->thread) {
/* gbinder_ipc_looper_thread() will release this reference: */
gbinder_ipc_looper_ref(looper);
GDEBUG("Starting looper %s", looper->name);
return looper;
} else {
GERR("Failed to create looper thread: %s", GERRMSG(error));
@@ -487,19 +782,85 @@ gbinder_ipc_looper_check(
if (G_LIKELY(self)) {
GBinderIpcPriv* priv = self->priv;
if (!priv->looper) {
if (!priv->primary_loopers) {
GBinderIpcLooper* looper;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (!priv->looper) {
GDEBUG("Starting looper %s", gbinder_ipc_name(self));
priv->looper = gbinder_ipc_looper_new(self);
looper = priv->primary_loopers;
if (!looper) {
looper = priv->primary_loopers = gbinder_ipc_looper_new(self);
}
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
/* We are not ready to accept incoming transactions until
* looper has started. We may need to wait a bit. */
if (looper && !g_atomic_int_get(&looper->started)) {
/* Lock */
g_mutex_lock(&looper->mutex);
if (!g_atomic_int_get(&looper->started)) {
g_cond_wait_until(&looper->start_cond, &looper->mutex,
g_get_monotonic_time() +
GBINDER_IPC_LOOPER_START_TIMEOUT_SEC *
G_TIME_SPAN_SECOND);
GASSERT(g_atomic_int_get(&looper->started));
}
g_mutex_unlock(&looper->mutex);
/* Unlock */
}
}
}
}
static
void
gbinder_ipc_looper_stop(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
GDEBUG("Stopping looper %s", looper->name);
g_atomic_int_set(&looper->exit, TRUE);
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
}
}
}
static
GBinderIpcLooper*
gbinder_ipc_looper_stop_all(
GBinderIpcLooper* loopers,
GBinderIpcLooper* list)
{
while (list) {
GBinderIpcLooper* looper = list;
GBinderIpcLooper* next = looper->next;
gbinder_ipc_looper_stop(looper);
looper->next = loopers;
loopers = looper;
list = next;
}
return loopers;
}
static
void
gbinder_ipc_looper_join(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
g_thread_join(looper->thread);
looper->thread = NULL;
}
looper->ipc = NULL;
}
/*==========================================================================*
* GBinderObjectRegistry
*==========================================================================*/
@@ -981,7 +1342,8 @@ gbinder_ipc_tx_proc(
GBinderIpc*
gbinder_ipc_new(
const char* dev)
const char* dev,
const GBinderRpcProtocol* protocol)
{
GBinderIpc* self = NULL;
@@ -994,7 +1356,7 @@ gbinder_ipc_new(
if (self) {
gbinder_ipc_ref(self);
} else {
GBinderDriver* driver = gbinder_driver_new(dev);
GBinderDriver* driver = gbinder_driver_new(dev, protocol);
if (driver) {
GBinderIpcPriv* priv;
@@ -1195,7 +1557,7 @@ gbinder_ipc_dispose(
{
GBinderIpc* self = GBINDER_IPC(object);
GBinderIpcPriv* priv = self->priv;
GBinderIpcLooper* looper;
GBinderIpcLooper* loopers = NULL;
GVERBOSE_("%s", self->dev);
/* Lock */
@@ -1216,22 +1578,19 @@ gbinder_ipc_dispose(
/* Lock */
g_mutex_lock(&priv->looper_mutex);
looper = priv->looper;
priv->looper = NULL;
loopers = gbinder_ipc_looper_stop_all(loopers, priv->primary_loopers);
loopers = gbinder_ipc_looper_stop_all(loopers, priv->blocked_loopers);
priv->blocked_loopers = NULL;
priv->primary_loopers = NULL;
g_mutex_unlock(&priv->looper_mutex);
/* Unlock */
if (looper) {
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
while (loopers) {
GBinderIpcLooper* looper = loopers;
GDEBUG("Stopping looper %s", gbinder_ipc_name(looper->ipc));
if (write(looper->pipefd[1], &done, sizeof(done)) > 0) {
g_thread_join(looper->thread);
looper->thread = NULL;
}
}
looper->ipc = NULL;
loopers = looper->next;
looper->next = NULL;
gbinder_ipc_looper_join(looper);
gbinder_ipc_looper_unref(looper);
}

View File

@@ -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
@@ -70,7 +70,8 @@ void
GBinderIpc*
gbinder_ipc_new(
const char* dev);
const char* dev,
const GBinderRpcProtocol* protocol);
GBinderIpc*
gbinder_ipc_ref(

View File

@@ -271,6 +271,7 @@ gbinder_local_object_handle_release_proc(
self->strong_refs--;
g_signal_emit(self, gbinder_local_object_signals
[SIGNAL_STRONG_REFS_CHANGED], 0);
gbinder_local_object_unref(self);
return G_SOURCE_REMOVE;
}
@@ -440,6 +441,7 @@ void
gbinder_local_object_handle_acquire(
GBinderLocalObject* self)
{
gbinder_local_object_ref(self);
gbinder_local_object_handle_later(self,
gbinder_local_object_handle_acquire_proc);
}
@@ -487,6 +489,7 @@ gbinder_local_object_finalize(
GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(local);
GBinderLocalObjectPriv* priv = self->priv;
GASSERT(!self->strong_refs);
gbinder_ipc_unref(self->ipc);
g_free(priv->iface);
G_OBJECT_CLASS(gbinder_local_object_parent_class)->finalize(local);

View File

@@ -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
@@ -33,6 +33,7 @@
#include "gbinder_local_reply_p.h"
#include "gbinder_output_data.h"
#include "gbinder_writer_p.h"
#include "gbinder_buffer_p.h"
#include "gbinder_log.h"
#include <gutil_intarray.h>
@@ -92,6 +93,19 @@ gbinder_local_reply_new(
return NULL;
}
GBinderLocalReply*
gbinder_local_reply_new_from_data(
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);
}
return self;
}
static
void
gbinder_local_reply_free(

View File

@@ -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
@@ -45,6 +45,10 @@ GBinderOutputData*
gbinder_local_reply_data(
GBinderLocalReply* reply);
GBinderLocalReply*
gbinder_local_reply_new_from_data(
GBinderBuffer* buffer);
#endif /* GBINDER_LOCAL_REPLY_PRIVATE_H */
/*

View File

@@ -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
@@ -33,6 +33,8 @@
#include "gbinder_local_request_p.h"
#include "gbinder_output_data.h"
#include "gbinder_writer_p.h"
#include "gbinder_buffer_p.h"
#include "gbinder_io.h"
#include "gbinder_log.h"
#include <gutil_intarray.h>
@@ -101,6 +103,19 @@ gbinder_local_request_new(
return NULL;
}
GBinderLocalRequest*
gbinder_local_request_new_from_data(
GBinderBuffer* buffer)
{
GBinderLocalRequest* self = gbinder_local_request_new
(gbinder_buffer_io(buffer), NULL);
if (self) {
gbinder_writer_data_set_contents(&self->data, buffer);
}
return self;
}
static
void
gbinder_local_request_free(

View File

@@ -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
@@ -46,6 +46,10 @@ GBinderOutputData*
gbinder_local_request_data(
GBinderLocalRequest* req);
GBinderLocalRequest*
gbinder_local_request_new_from_data(
GBinderBuffer* buffer);
#endif /* GBINDER_LOCAL_REQUEST_PRIVATE_H */
/*

View File

@@ -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,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);
@@ -269,10 +325,9 @@ gbinder_reader_read_buffer_impl(
GBinderBuffer** 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;
@@ -305,6 +360,77 @@ gbinder_reader_skip_buffer(
return gbinder_reader_read_buffer_impl(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 */
{
const void* result = NULL;
GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
/* Check the size */
if (buf && buf->size == size) {
result = buf->data;
}
gbinder_buffer_free(buf);
return result;
}
/* Doesn't copy the data */
const void*
gbinder_reader_read_hidl_vec(
GBinderReader* reader,
gsize* count,
gsize* elemsize)
{
GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
gsize out_count = 0, out_elemsize = 0;
const void* out = NULL;
if (buf && buf->size == sizeof(GBinderHidlVec)) {
const GBinderHidlVec* vec = buf->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;
out_count = vec->count;
out = vbuf->data;
}
gbinder_buffer_free(vbuf);
} else if (!vec->count) {
/* Any non-NULL pointer just to indicate success */
out = vec;
}
}
gbinder_buffer_free(buf);
if (elemsize) {
*elemsize = out_elemsize;
}
if (count) {
*count = out_count;
}
return out;
}
/* Helper for gbinder_reader_read_hidl_struct_vec() macro */
const void*
gbinder_reader_read_hidl_vec1(
GBinderReader* reader,
gsize* count,
guint expected_elem_size) /* since 1.0.9 */
{
gsize actual;
const void* data = gbinder_reader_read_hidl_vec(reader, count, &actual);
/* Actual size will be zero for an empty array */
return (data && (actual == expected_elem_size || !actual)) ? data : NULL;
}
char*
gbinder_reader_read_hidl_string(
GBinderReader* reader)
@@ -312,8 +438,8 @@ gbinder_reader_read_hidl_string(
GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
char* str = NULL;
if (buf && buf->size == sizeof(HidlString)) {
const HidlString* s = buf->data;
if (buf && buf->size == sizeof(GBinderHidlString)) {
const GBinderHidlString* s = buf->data;
GBinderBuffer* sbuf = gbinder_reader_read_buffer(reader);
if (sbuf && sbuf->size == s->len + 1 &&
@@ -334,8 +460,8 @@ gbinder_reader_read_hidl_string_vec(
GBinderBuffer* buf = gbinder_reader_read_buffer(reader);
/* First buffer contains hidl_vector */
if (buf && buf->size == sizeof(HidlVec)) {
HidlVec* vec = buf->data;
if (buf && buf->size == sizeof(GBinderHidlVec)) {
GBinderHidlVec* vec = buf->data;
const guint n = vec->count;
const void* next = vec->data.ptr;
@@ -348,8 +474,9 @@ gbinder_reader_read_hidl_string_vec(
} 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;
if (buf && buf->data == next &&
buf->size == (sizeof(GBinderHidlString) * n)) {
const GBinderHidlString* strings = buf->data;
GBinderBuffer* sbuf;
GPtrArray* list = g_ptr_array_new();
guint i;
@@ -357,7 +484,7 @@ gbinder_reader_read_hidl_string_vec(
/* 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;
const GBinderHidlString* s = strings + i;
if (sbuf->size == s->len + 1 &&
sbuf->data == s->data.str &&
s->data.str[s->len] == 0) {
@@ -418,6 +545,24 @@ gboolean
gbinder_reader_read_nullable_string16(
GBinderReader* reader,
char** out)
{
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,
gunichar2** out,
gsize* out_len) /* since 1.0.17 */
{
GBinderReaderPriv* p = gbinder_reader_cast(reader);
@@ -431,15 +576,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);
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;
}
@@ -484,24 +635,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

View File

@@ -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
@@ -31,9 +31,10 @@
*/
#include "gbinder_remote_reply_p.h"
#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>
@@ -64,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);
}
}
@@ -117,6 +114,18 @@ gbinder_remote_reply_is_empty(
return !self || !self->data.buffer || !self->data.buffer->size;
}
GBinderLocalReply*
gbinder_remote_reply_copy_to_local(
GBinderRemoteReply* self)
{
if (G_LIKELY(self)) {
GBinderReaderData* d = &self->data;
return gbinder_local_reply_new_from_data(d->buffer);
}
return NULL;
}
static
inline
void

View File

@@ -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(

View File

@@ -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,13 +33,17 @@
#include "gbinder_remote_request_p.h"
#include "gbinder_reader_p.h"
#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;
@@ -48,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(
@@ -57,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);
@@ -65,28 +73,47 @@ 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* 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);
}
return NULL;
}
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 */
@@ -103,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)) {
@@ -160,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 {
@@ -172,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);
}
@@ -194,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;
@@ -216,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;
@@ -230,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;
@@ -243,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;
@@ -256,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;

View File

@@ -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 */

View File

@@ -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
@@ -69,9 +69,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;
@@ -99,6 +103,7 @@ static
const char*
gbinder_rpc_protocol_hwbinder_read_rpc_header(
GBinderReader* reader,
guint32 txcode,
char** iface)
{
*iface = NULL;
@@ -109,22 +114,22 @@ gbinder_rpc_protocol_hwbinder_read_rpc_header(
* Interface
*==========================================================================*/
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
};
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
};
const GBinderRpcProtocol*
gbinder_rpc_protocol_for_device(
const char* dev)
{
static const GBinderRpcProtocol protocol_binder = {
.read_rpc_header = gbinder_rpc_protocol_binder_read_rpc_header,
.write_rpc_header = gbinder_rpc_protocol_binder_write_rpc_header
};
static const GBinderRpcProtocol protocol_hwbinder = {
.read_rpc_header = gbinder_rpc_protocol_hwbinder_read_rpc_header,
.write_rpc_header = gbinder_rpc_protocol_hwbinder_write_rpc_header
};
return (dev && !strcmp(dev, GBINDER_DEFAULT_HWBINDER)) ?
&protocol_hwbinder : &protocol_binder;
&gbinder_rpc_protocol_hwbinder : &gbinder_rpc_protocol_binder;
}
/*

View File

@@ -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,10 @@
* 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.
* 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
@@ -41,10 +42,15 @@
*/
struct gbinder_rpc_protocol {
const char* (*read_rpc_header)(GBinderReader* reader, char** iface);
const char* (*read_rpc_header)(GBinderReader* reader, guint32 txcode,
char** iface);
void (*write_rpc_header)(GBinderWriter* writer, const char* iface);
};
extern const GBinderRpcProtocol gbinder_rpc_protocol_binder;
extern const GBinderRpcProtocol gbinder_rpc_protocol_hwbinder;
/* Returns one of the above based on the device name */
const GBinderRpcProtocol*
gbinder_rpc_protocol_for_device(
const char* dev);

View File

@@ -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,17 @@
#include <errno.h>
typedef struct gbinder_servicemanager_watch {
char* name;
char* detail;
GQuark quark;
gboolean watched;
} GBinderServiceManagerWatch;
struct gbinder_servicemanager_priv {
GHashTable* watch_table;
};
G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager,
G_TYPE_OBJECT)
@@ -60,6 +71,16 @@ 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_REGISTRATION,
SIGNAL_COUNT
};
static const char SIGNAL_REGISTRATION_NAME[] = "servicemanager-registration";
#define DETAIL_LEN 32
static guint gbinder_servicemanager_signals[SIGNAL_COUNT] = { 0 };
/*==========================================================================*
* Implementation
*==========================================================================*/
@@ -81,53 +102,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);
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 {
@@ -256,6 +253,89 @@ gbinder_servicemanager_add_service_tx_free(
g_slice_free(GBinderServiceManagerAddServiceTxData, data);
}
/*==========================================================================*
* 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) {
GBinderRemoteObject* object = gbinder_ipc_get_remote_object
(ipc, klass->handle);
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;
}
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
*==========================================================================*/
@@ -433,6 +513,88 @@ gbinder_servicemanager_cancel(
}
}
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) {
watch->watched = klass->watch(self, 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 */
{
if (G_LIKELY(self) && G_LIKELY(id)) {
GBinderServiceManagerClass* klass =
GBINDER_SERVICEMANAGER_GET_CLASS(self);
GBinderServiceManagerPriv* priv = self->priv;
GHashTableIter it;
gpointer value;
g_signal_handler_disconnect(self, id);
g_hash_table_iter_init(&it, priv->watch_table);
while (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 the one we have just removed */
GDEBUG("Unwatching %s", watch->name);
watch->watched = FALSE;
klass->unwatch(self, watch->name);
break;
}
}
}
}
/*==========================================================================*
* Internals
*==========================================================================*/
@@ -442,6 +604,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 +618,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 */
@@ -475,7 +642,7 @@ gbinder_servicemanager_dispose(
* gbinder_servicemanager_finalize() will not be called
* this time around.
*/
if (klass->table && object->ref_count == 0) {
if (klass->table && g_atomic_int_get(&object->ref_count) <= 1) {
g_hash_table_remove(klass->table, self->dev);
if (g_hash_table_size(klass->table) == 0) {
g_hash_table_unref(klass->table);
@@ -493,9 +660,10 @@ 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);
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);
}
@@ -508,8 +676,13 @@ gbinder_servicemanager_class_init(
GObjectClass* object_class = G_OBJECT_CLASS(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_REGISTRATION] =
g_signal_new(SIGNAL_REGISTRATION_NAME, G_OBJECT_CLASS_TYPE(klass),
G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_STRING);
}
/*

View File

@@ -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,13 +39,22 @@
#include <glib-object.h>
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;
@@ -54,6 +63,7 @@ typedef struct gbinder_servicemanager_class {
guint32 handle;
const char* iface;
const char* default_device;
const GBinderRpcProtocol* rpc_protocol;
/* Methods (synchronous) */
char** (*list)(GBinderServiceManager* self);
@@ -62,6 +72,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);
@@ -72,6 +91,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 */
/*

268
src/gbinder_servicepoll.c Normal file
View 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
View 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:
*/

View File

@@ -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
@@ -35,6 +35,7 @@
#include <gbinder_types.h>
typedef struct gbinder_buffer_contents GBinderBufferContents;
typedef struct gbinder_cleanup GBinderCleanup;
typedef struct gbinder_driver GBinderDriver;
typedef struct gbinder_handler GBinderHandler;
@@ -43,28 +44,8 @@ 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

View File

@@ -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
@@ -31,6 +31,7 @@
*/
#include "gbinder_writer_p.h"
#include "gbinder_buffer_p.h"
#include "gbinder_io.h"
#include "gbinder_log.h"
@@ -38,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;
@@ -51,6 +55,55 @@ GBINDER_INLINE_FUNC GBinderWriterPriv* gbinder_writer_cast(GBinderWriter* pub)
GBINDER_INLINE_FUNC GBinderWriterData* gbinder_writer_data(GBinderWriter* pub)
{ return G_LIKELY(pub) ? gbinder_writer_cast(pub)->data : NULL; }
static
void
gbinder_writer_data_buffer_cleanup(
gpointer data)
{
gbinder_buffer_contents_unref((GBinderBufferContents*)data);
}
void
gbinder_writer_data_set_contents(
GBinderWriterData* data,
GBinderBuffer* buffer)
{
gsize bufsize;
const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize);
const GBinderIo* io = gbinder_buffer_io(buffer);
GBinderBufferContents* contents = gbinder_buffer_contents(buffer);
GASSERT(data->io == io);
g_byte_array_set_size(data->bytes, 0);
gutil_int_array_set_count(data->offsets, 0);
data->buffers_size = 0;
gbinder_cleanup_reset(data->cleanup);
g_byte_array_append(data->bytes, bufdata, bufsize);
if (contents) {
void** objects = gbinder_buffer_objects(buffer);
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);
}
}
}
}
static
void
gbinder_writer_data_record_offset(
@@ -71,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 */
@@ -117,7 +170,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));
}
@@ -138,10 +191,11 @@ 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;
}
@@ -162,10 +216,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;
}
@@ -186,10 +241,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;
}
@@ -210,10 +266,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;
}
@@ -253,15 +310,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 */
@@ -298,14 +356,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;
@@ -316,14 +396,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
@@ -350,17 +432,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);
}
}
@@ -388,6 +525,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,
@@ -442,18 +633,66 @@ gbinder_writer_append_hidl_string(
}
}
void
gbinder_writer_data_append_hidl_vec(
GBinderWriterData* data,
const void* base,
guint count,
guint elemsize)
{
GBinderParent vec_parent;
GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
const gsize total = count * elemsize;
void* buf = g_memdup(base, total);
/* Prepare parent descriptor for the string data */
vec_parent.index = gbinder_writer_data_prepare(data);
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
/* Fill in the vector descriptor */
if (buf) {
vec->data.ptr = buf;
vec->count = count;
data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, buf);
}
vec->owns_buffer = TRUE;
data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, vec);
/* Write the buffer object pointing to the vector descriptor */
gbinder_writer_data_write_buffer_object(data, vec, sizeof(*vec), NULL);
/* Not sure what's the right way to deal with NULL vectors... */
if (buf) {
gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
}
}
void
gbinder_writer_append_hidl_vec(
GBinderWriter* self,
const void* base,
guint count,
guint elemsize)
{
GBinderWriterData* data = gbinder_writer_data(self);
if (G_LIKELY(data)) {
gbinder_writer_data_append_hidl_vec(data, base, count, elemsize);
}
}
void
gbinder_writer_data_append_hidl_string(
GBinderWriterData* data,
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;
@@ -495,8 +734,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) {
@@ -506,11 +745,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);
}
@@ -521,7 +760,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);
@@ -536,7 +775,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,
@@ -544,7 +783,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,
@@ -553,7 +792,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);
}
}
}
@@ -575,16 +814,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);
}
@@ -601,25 +840,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

View File

@@ -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
@@ -50,6 +50,11 @@ gbinder_writer_init(
GBinderWriter* writer,
GBinderWriterData* data);
void
gbinder_writer_data_set_contents(
GBinderWriterData* data,
GBinderBuffer* buffer);
void
gbinder_writer_data_append_bool(
GBinderWriterData* data,
@@ -104,6 +109,13 @@ gbinder_writer_data_append_buffer_object(
gsize size,
const GBinderParent* parent);
void
gbinder_writer_data_append_hidl_vec(
GBinderWriterData* data,
const void* base,
guint count,
guint elemsize);
void
gbinder_writer_data_append_hidl_string(
GBinderWriterData* data,

View File

@@ -3,6 +3,7 @@
all:
%:
@$(MAKE) -C binder-client $*
@$(MAKE) -C binder-dump $*
@$(MAKE) -C binder-list $*
@$(MAKE) -C binder-service $*
@$(MAKE) -C rild-card-status $*

View File

@@ -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
View File

@@ -0,0 +1,140 @@
# -*- Mode: makefile-gmake -*-
.PHONY: all debug release clean cleaner
.PHONY: libgbinder-release libgbinder-debug
#
# Required packages
#
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
#
# Default target
#
all: debug release
#
# Executable
#
EXE = 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)

View 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:
*/

View File

@@ -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 }

View File

@@ -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
@@ -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
@@ -175,6 +244,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 }
};

View File

@@ -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,8 @@ all:
@$(MAKE) -C unit_remote_object $*
@$(MAKE) -C unit_remote_reply $*
@$(MAKE) -C unit_remote_request $*
@$(MAKE) -C unit_servicemanager $*
@$(MAKE) -C unit_servicepoll $*
@$(MAKE) -C unit_writer $*
clean: unitclean

View File

@@ -103,9 +103,13 @@ ifneq ($(strip $(DEPS)),)
endif
endif
$(DEBUG_EXE) $(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_EXE) $(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
$(COVERAGE_EXE) $(COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR)
$(DEBUG_LIB): | debug_lib
$(RELEASE_LIB): | release_lib
$(COVERAGE_LIB): | coverage_lib
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
$(COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR)
#
# Rules
@@ -166,13 +170,13 @@ $(RELEASE_BUILD_DIR)/common_%.o : $(COMMON_DIR)/%.c
$(COVERAGE_BUILD_DIR)/common_%.o : $(COMMON_DIR)/%.c
$(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_EXE): $(DEBUG_PLUGIN_LIB) $(DEBUG_OBJS)
$(DEBUG_EXE): $(DEBUG_LIB) $(DEBUG_OBJS)
$(LD) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) $(DEBUG_LIBS) -o $@
$(RELEASE_EXE): $(RELEASE_PLUGIN_LIB) $(RELEASE_OBJS)
$(RELEASE_EXE): $(RELEASE_LIB) $(RELEASE_OBJS)
$(LD) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) $(RELEASE_LIBS) -o $@
$(COVERAGE_EXE): $(COVERAGE_PLUGIN_LIB) $(COVERAGE_OBJS)
$(COVERAGE_EXE): $(COVERAG_LIB) $(COVERAGE_OBJS)
$(LD) $(COVERAGE_LDFLAGS) $(COVERAGE_OBJS) $(COVERAGE_LIBS) -o $@
debug_lib:

View File

@@ -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
@@ -32,7 +32,9 @@
#include "gbinder_system.h"
#define GLOG_MODULE_NAME test_binder_log
#include <gutil_log.h>
GLOG_MODULE_DEFINE2("test_binder", gutil_log_default);
#include <unistd.h>
#include <fcntl.h>
@@ -61,6 +63,7 @@ typedef struct test_binder_node {
char* path;
int refcount;
const TestBinderIo* io;
GHashTable* destroy_map;
} TestBinderNode;
typedef struct test_binder {
@@ -123,6 +126,25 @@ typedef struct binder_pre_cookie_64 {
#define BR_DEAD_BINDER_64 _IOR('r', 15, guint64)
#define BR_FAILED_REPLY _IO('r', 17)
static
void
test_io_free_buffer(
TestBinder* binder,
void* ptr)
{
if (ptr) {
TestBinderNode* node = binder->node;
GDestroyNotify destroy = g_hash_table_lookup(node->destroy_map, ptr);
if (destroy) {
g_hash_table_remove(node->destroy_map, ptr);
destroy(ptr);
} else {
g_free(ptr);
}
}
}
static
int
test_io_handle_write_read_64(
@@ -151,7 +173,8 @@ test_io_handle_write_read_64(
/* Is there anything special about transactions and replies? */
break;
case BC_FREE_BUFFER_64:
g_free((void*)(gsize)(*(guint64*)write_ptr));
test_io_free_buffer(binder,
GSIZE_TO_POINTER(*(guint64*)write_ptr));
break;
case BC_INCREFS:
case BC_ACQUIRE:
@@ -219,6 +242,7 @@ test_binder_node_unref(
node->refcount--;
if (!node->refcount) {
g_hash_table_remove(test_node_map, node->path);
g_hash_table_destroy(node->destroy_map);
g_free(node->path);
g_free(node);
}
@@ -228,24 +252,57 @@ test_binder_node_unref(
}
}
static
TestBinder*
test_binder_from_fd(
int fd)
{
TestBinder* binder = NULL;
GASSERT(test_fd_map);
if (test_fd_map) {
binder = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
GASSERT(binder);
}
return binder;
}
static
void
test_io_destroy_none(
gpointer data)
{
GDEBUG("Not freeing %p", data);
}
void
test_binder_set_destroy(
int fd,
gpointer ptr,
GDestroyNotify destroy)
{
TestBinder* binder = test_binder_from_fd(fd);
if (binder) {
TestBinderNode* node = binder->node;
g_hash_table_replace(node->destroy_map, ptr,
destroy ? destroy : test_io_destroy_none);
}
}
static
gboolean
test_binder_push_data(
int fd,
const void* data)
{
GASSERT(test_fd_map);
if (test_fd_map) {
gpointer key = GINT_TO_POINTER(fd);
TestBinder* binder = g_hash_table_lookup(test_fd_map, key);
TestBinder* binder = test_binder_from_fd(fd);
GASSERT(binder);
if (binder) {
const guint32* cmd = data;
const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
if (binder) {
const guint32* cmd = data;
const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
return write(binder->private_fd, data, len) == len;
}
return write(binder->private_fd, data, len) == len;
}
return FALSE;
}
@@ -440,6 +497,7 @@ gbinder_system_open(
node->path = g_strdup(path);
node->refcount = 1;
node->io = &test_io_64;
node->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
if (!test_node_map) {
test_node_map = g_hash_table_new(g_str_hash, g_str_equal);
}
@@ -466,24 +524,19 @@ int
gbinder_system_close(
int fd)
{
GASSERT(test_fd_map);
if (test_fd_map) {
gpointer key = GINT_TO_POINTER(fd);
TestBinder* binder = g_hash_table_lookup(test_fd_map, key);
TestBinder* binder = test_binder_from_fd(fd);
GASSERT(binder);
if (binder) {
g_hash_table_remove(test_fd_map, key);
if (!g_hash_table_size(test_fd_map)) {
g_hash_table_unref(test_fd_map);
test_fd_map = NULL;
}
test_binder_node_unref(binder->node);
close(binder->public_fd);
close(binder->private_fd);
g_free(binder);
return 0;
if (binder) {
g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd));
if (!g_hash_table_size(test_fd_map)) {
g_hash_table_unref(test_fd_map);
test_fd_map = NULL;
}
test_binder_node_unref(binder->node);
close(binder->public_fd);
close(binder->private_fd);
g_free(binder);
return 0;
}
errno = EBADF;
return -1;
@@ -495,27 +548,21 @@ gbinder_system_ioctl(
int request,
void* data)
{
GASSERT(test_fd_map);
if (test_fd_map) {
gpointer key = GINT_TO_POINTER(fd);
TestBinder* binder = g_hash_table_lookup(test_fd_map, key);
TestBinder* binder = test_binder_from_fd(fd);
if (binder) {
const TestBinderIo* io = binder->node->io;
GASSERT(binder);
if (binder) {
const TestBinderIo* io = binder->node->io;
switch (request) {
case BINDER_VERSION:
return test_binder_ioctl_version(binder, data);
case BINDER_SET_MAX_THREADS:
return 0;
default:
if (request == io->write_read_request) {
return io->handle_write_read(binder, data);
} else {
errno = EINVAL;
return -1;
}
switch (request) {
case BINDER_VERSION:
return test_binder_ioctl_version(binder, data);
case BINDER_SET_MAX_THREADS:
return 0;
default:
if (request == io->write_read_request) {
return io->handle_write_read(binder, data);
} else {
errno = EINVAL;
return -1;
}
}
}

View File

@@ -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
@@ -95,6 +95,12 @@ test_binder_br_reply_status(
int fd,
gint32 status);
void
test_binder_set_destroy(
int fd,
gpointer ptr,
GDestroyNotify destroy);
#endif /* TEST_BINDER_H */
/*

View File

@@ -5,6 +5,7 @@
TESTS="\
unit_buffer \
unit_cleanup \
unit_client \
unit_driver \
unit_ipc \
@@ -16,6 +17,8 @@ unit_reader \
unit_remote_object \
unit_remote_reply \
unit_remote_request \
unit_servicemanager \
unit_servicepoll \
unit_writer"
function err() {

View File

@@ -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
@@ -46,19 +46,22 @@ void
test_null(
void)
{
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* buf = gbinder_buffer_new(NULL, NULL, 0);
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
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);
@@ -66,6 +69,11 @@ 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));
g_assert(!size);
gbinder_driver_unref(driver);
}
@@ -80,12 +88,20 @@ test_parent(
{
static const guint8 data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
void* ptr = g_memdup(data, sizeof(data));
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* parent = gbinder_buffer_new(driver, ptr, sizeof(data));
gsize size = 0;
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
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_contents(buf));
g_assert(gbinder_buffer_data(buf, NULL) == ptr);
g_assert(gbinder_buffer_data(buf, &size) == ptr);
g_assert(size == sizeof(data));
gbinder_buffer_free(buf);
gbinder_buffer_free(parent);
gbinder_driver_unref(driver);

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_cleanup
include ../common/Makefile

View 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:
*/

View File

@@ -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,7 +54,7 @@ test_client_new(
guint handle,
const char* iface)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, handle);
GBinderClient* client = gbinder_client_new(obj, iface);
@@ -95,12 +95,11 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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");
g_assert(!gbinder_client_new(obj, NULL));
g_assert(client);
g_assert(gbinder_client_ref(client) == client);
gbinder_client_unref(client);
@@ -111,6 +110,25 @@ 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);
}
/*==========================================================================*
* sync_oneway
*==========================================================================*/
@@ -326,17 +344,19 @@ 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_("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();
}

View File

@@ -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
@@ -56,8 +56,8 @@ test_basic(
GBinderDriver* driver;
const char* dev = GBINDER_DEFAULT_BINDER;
g_assert(!gbinder_driver_new(""));
driver = gbinder_driver_new(dev);
g_assert(!gbinder_driver_new("", NULL));
driver = gbinder_driver_new(dev, NULL);
g_assert(driver);
g_assert(!g_strcmp0(dev, gbinder_driver_dev(driver)));
g_assert(gbinder_driver_ref(driver) == driver);
@@ -86,7 +86,7 @@ void
test_noop(
void)
{
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const int fd = gbinder_driver_fd(driver);
g_assert(driver);
@@ -116,7 +116,7 @@ test_local_request(
0x00, 0x00, 0x00, 0x00
};
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderLocalRequest* req = gbinder_driver_local_request_new(driver, iface);
GBinderOutputData* data = gbinder_local_request_data(req);

View File

@@ -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
@@ -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
*==========================================================================*/
@@ -91,8 +109,8 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
g_assert(ipc);
g_assert(ipc2);
@@ -101,14 +119,14 @@ test_basic(
gbinder_ipc_unref(ipc2);
/* Second gbinder_ipc_new returns the same (default) object */
g_assert(gbinder_ipc_new(NULL) == ipc);
g_assert(gbinder_ipc_new("") == ipc);
g_assert(gbinder_ipc_new(NULL, NULL) == ipc);
g_assert(gbinder_ipc_new("", NULL) == ipc);
gbinder_ipc_unref(ipc);
gbinder_ipc_unref(ipc);
gbinder_ipc_unref(ipc);
/* Invalid path */
g_assert(!gbinder_ipc_new("invalid path"));
g_assert(!gbinder_ipc_new("invalid path", NULL));
}
/*==========================================================================*
@@ -120,7 +138,7 @@ void
test_sync_oneway(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -141,7 +159,7 @@ void
test_sync_reply_ok_status(
int* status)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -196,7 +214,7 @@ void
test_sync_reply_error(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -253,7 +271,7 @@ void
test_transact_ok(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -310,7 +328,7 @@ void
test_transact_dead(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -356,7 +374,7 @@ void
test_transact_failed(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -404,7 +422,7 @@ void
test_transact_status(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -445,7 +463,7 @@ void
test_transact_custom(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_ipc_transact_custom(ipc, NULL,
test_transact_custom_done, NULL, loop);
@@ -475,7 +493,7 @@ void
test_transact_custom2(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_ipc_transact_custom(ipc, NULL, NULL,
test_transact_custom_destroy, loop);
@@ -522,7 +540,7 @@ void
test_transact_cancel(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel_exec,
test_transact_cancel_done, test_transact_cancel_destroy, loop);
@@ -566,7 +584,7 @@ void
test_transact_cancel2(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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_cancel2_exec,
@@ -606,30 +624,12 @@ 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(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -651,17 +651,17 @@ test_transact_incoming(
/* 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);
g_main_loop_unref(loop);
}
/*==========================================================================*
* transact_incoming_status
* transact_status_reply
*==========================================================================*/
static
@@ -690,7 +690,7 @@ void
test_transact_status_reply(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -712,10 +712,184 @@ test_transact_status_reply(
/* 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);
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);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, "test", 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_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);
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);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, "test", 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_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);
g_main_loop_unref(loop);
@@ -726,26 +900,28 @@ 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_("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_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);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -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
@@ -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,7 +150,7 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderLocalObject* foo;
GBinderLocalObject* bar;
@@ -186,6 +187,49 @@ test_basic(
gbinder_local_object_unref(bar);
}
/*==========================================================================*
* ping
*==========================================================================*/
static
void
test_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_ipc_new_local_object(ipc, NULL, NULL, NULL);
GBinderLocalReply* reply;
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);
gbinder_ipc_unref(ipc);
gbinder_local_object_unref(obj);
gbinder_local_reply_unref(reply);
gbinder_remote_request_unref(req);
}
/*==========================================================================*
* get_descriptor
*==========================================================================*/
@@ -195,21 +239,20 @@ 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);
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);
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);
@@ -256,15 +299,16 @@ test_descriptor_chain(
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);
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);
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_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER);
@@ -323,7 +367,7 @@ test_custom_iface(
int count = 0, status = INT_MAX;
const char* dev = GBINDER_DEFAULT_HWBINDER;
const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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,
@@ -334,8 +378,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,
@@ -428,14 +473,15 @@ test_reply_status(
int count = 0, status = 0;
const char* dev = GBINDER_DEFAULT_HWBINDER;
const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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,
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,
@@ -468,7 +514,7 @@ void
test_increfs(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -510,7 +556,7 @@ void
test_decrefs(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -552,7 +598,7 @@ void
test_acquire(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -594,7 +640,7 @@ void
test_release(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderLocalObject* obj = gbinder_ipc_new_local_object
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -627,6 +673,7 @@ 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 "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);

View File

@@ -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
@@ -31,10 +31,13 @@
*/
#include "test_common.h"
#include "test_binder.h"
#include "gbinder_local_object.h"
#include "gbinder_local_reply_p.h"
#include "gbinder_output_data.h"
#include "gbinder_buffer_p.h"
#include "gbinder_driver.h"
#include "gbinder_writer.h"
#include "gbinder_io.h"
#include "gbinder_ipc.h"
@@ -56,6 +59,17 @@ test_int_inc(
(*((int*)data))++;
}
static
GBinderBuffer*
test_buffer_from_bytes(
GBinderDriver* driver,
const GByteArray* bytes)
{
/* Prevent double free */
test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL);
}
/*==========================================================================*
* null
*==========================================================================*/
@@ -74,6 +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));
gbinder_local_reply_cleanup(NULL, NULL, &count);
gbinder_local_reply_cleanup(NULL, test_int_inc, &count);
@@ -122,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;
@@ -344,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);
}
@@ -367,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);
}
@@ -384,7 +399,7 @@ test_local_object(
GBinderLocalReply* reply;
GBinderOutputData* data;
GUtilIntArray* offsets;
GBinderIpc* ipc = gbinder_ipc_new(NULL);
GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL);
GBinderLocalObject* obj =
gbinder_ipc_new_local_object(ipc, "foo", NULL, NULL);
@@ -438,6 +453,47 @@ test_remote_object(
gbinder_local_reply_unref(reply);
}
/*==========================================================================*
* remote_reply
*==========================================================================*/
static
void
test_remote_reply(
void)
{
/* The size of the string gets aligned at 4-byte boundary */
static const char input[] = "test";
static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 };
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(driver);
GBinderLocalReply* req = gbinder_local_reply_new(io);
GBinderLocalReply* req2;
GBinderOutputData* data2;
const GByteArray* bytes;
const GByteArray* bytes2;
GBinderBuffer* buffer;
gbinder_local_reply_append_string8(req, input);
bytes = gbinder_local_reply_data(req)->bytes;
/* Copy flat structures (no binder objects) */
buffer = test_buffer_from_bytes(driver, bytes);
req2 = gbinder_local_reply_new_from_data(buffer);
gbinder_buffer_free(buffer);
data2 = gbinder_local_reply_data(req2);
bytes2 = data2->bytes;
g_assert(!gbinder_output_data_offsets(data2));
g_assert(!gbinder_output_data_buffers_size(data2));
g_assert(bytes2->len == sizeof(output));
g_assert(!memcmp(bytes2->data, output, bytes2->len));
gbinder_local_reply_unref(req2);
gbinder_local_reply_unref(req);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* Common
*==========================================================================*/
@@ -460,6 +516,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_PREFIX "hidl_string_vec", test_hidl_string_vec);
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_PREFIX "remote_reply", test_remote_reply);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -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
@@ -31,9 +31,12 @@
*/
#include "test_common.h"
#include "test_binder.h"
#include "gbinder_local_request_p.h"
#include "gbinder_output_data.h"
#include "gbinder_buffer_p.h"
#include "gbinder_driver.h"
#include "gbinder_writer.h"
#include "gbinder_io.h"
@@ -54,6 +57,29 @@ test_int_inc(
(*((int*)data))++;
}
static
GBinderBuffer*
test_buffer_from_bytes(
GBinderDriver* driver,
const GByteArray* bytes)
{
/* Prevent double free */
test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
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);
}
/*==========================================================================*
* null
*==========================================================================*/
@@ -68,6 +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));
gbinder_local_request_unref(NULL);
gbinder_local_request_init_writer(NULL, NULL);
gbinder_local_request_init_writer(NULL, &writer);
@@ -152,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;
@@ -360,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);
}
@@ -383,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);
}
@@ -436,6 +463,126 @@ test_remote_object(
gbinder_local_request_unref(req);
}
/*==========================================================================*
* remote_request
*==========================================================================*/
static
void
test_remote_request(
void)
{
/* The size of the string gets aligned at 4-byte boundary */
static const char input[] = "test";
static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 };
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GBinderLocalRequest* req2;
GBinderOutputData* data2;
const GByteArray* bytes;
const GByteArray* bytes2;
GBinderBuffer* buffer;
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);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
bytes2 = data2->bytes;
g_assert(!gbinder_output_data_offsets(data2));
g_assert(!gbinder_output_data_buffers_size(data2));
g_assert(bytes2->len == sizeof(output));
g_assert(!memcmp(bytes2->data, output, bytes2->len));
gbinder_local_request_unref(req2);
/* Same thing but with non-NULL (albeit empty) array of objects */
buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj);
req2 = gbinder_local_request_new_from_data(buffer);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
bytes2 = data2->bytes;
g_assert(!gbinder_output_data_offsets(data2));
g_assert(!gbinder_output_data_buffers_size(data2));
g_assert(bytes2->len == sizeof(output));
g_assert(!memcmp(bytes2->data, output, bytes2->len));
gbinder_local_request_unref(req2);
gbinder_local_request_unref(req);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* remote_request_obj
*==========================================================================*/
static
void
test_remote_request_obj_validate_data(
GBinderOutputData* data)
{
const GByteArray* bytes = data->bytes;
GUtilIntArray* offsets = gbinder_output_data_offsets(data);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
g_assert(offsets->count == 3);
g_assert(offsets->data[0] == 4);
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);
/* GBinderHidlString + the contents (2 bytes) aligned at 8-byte boundary */
g_assert(gbinder_output_data_buffers_size(data) ==
(sizeof(GBinderHidlString) + 8));
}
static
void
test_remote_request_obj(
void)
{
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GBinderLocalRequest* req2;
GBinderOutputData* data;
GUtilIntArray* offsets;
GBinderBuffer* buffer;
const GByteArray* bytes;
void** objects;
guint i;
gbinder_local_request_append_int32(req, 1);
gbinder_local_request_append_hidl_string(req, "2");
gbinder_local_request_append_local_object(req, NULL);
data = gbinder_local_request_data(req);
test_remote_request_obj_validate_data(data);
bytes = data->bytes;
offsets = gbinder_output_data_offsets(data);
objects = g_new0(void*, offsets->count + 1);
for (i = 0; i < offsets->count; i++) {
objects[i] = bytes->data + offsets->data[i];
}
buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects);
req2 = gbinder_local_request_new_from_data(buffer);
gbinder_buffer_free(buffer);
test_remote_request_obj_validate_data(gbinder_local_request_data(req2));
/* req2 has to be freed first because req owns data */
gbinder_local_request_unref(req2);
gbinder_local_request_unref(req);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* Common
*==========================================================================*/
@@ -459,6 +606,8 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_PREFIX "hidl_string_vec", test_hidl_string_vec);
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_PREFIX "remote_request", test_remote_request);
g_test_add_func(TEST_PREFIX "remote_request_obj", test_remote_request_obj);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -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
@@ -74,39 +74,61 @@ static const TestHeaderData test_header_tests[] = {
};
/*==========================================================================*
* default
* device
*==========================================================================*/
static
void
test_default(
test_device(
void)
{
const GBinderRpcProtocol* p1 = gbinder_rpc_protocol_for_device(NULL);
const GBinderRpcProtocol* p2 = gbinder_rpc_protocol_for_device
(GBINDER_DEFAULT_BINDER);
g_assert(p1);
g_assert(p1 == p2);
g_assert(gbinder_rpc_protocol_for_device(NULL) ==
&gbinder_rpc_protocol_binder);
g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER) ==
&gbinder_rpc_protocol_binder);
g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_HWBINDER) ==
&gbinder_rpc_protocol_hwbinder);
}
/*==========================================================================*
* 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
*==========================================================================*/
@@ -140,42 +162,43 @@ test_read_header(
gconstpointer test_data)
{
const TestHeaderData* test = test_data;
GBinderDriver* driver = gbinder_driver_new(test->dev);
GBinderDriver* driver = gbinder_driver_new(test->dev, NULL);
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 "default", test_default);
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

View File

@@ -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
@@ -67,7 +67,7 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg, 1);
GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg, 2);
@@ -108,7 +108,7 @@ test_dead(
{
const guint handle = 1;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderRemoteObject* obj = gbinder_ipc_get_remote_object(ipc, handle);
gulong id = gbinder_remote_object_add_death_handler
(obj, test_dead_done, loop);

View File

@@ -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
@@ -35,10 +35,17 @@
#include "gbinder_buffer_p.h"
#include "gbinder_driver.h"
#include "gbinder_reader.h"
#include "gbinder_local_reply_p.h"
#include "gbinder_remote_reply_p.h"
#include "gbinder_output_data.h"
#include <gutil_intarray.h>
static TestOpt test_opt;
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', 0x85)
#define BINDER_OBJECT_SIZE_64 (GBINDER_MAX_BINDER_OBJECT_SIZE)
/*==========================================================================*
* null
*==========================================================================*/
@@ -52,10 +59,11 @@ 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));
g_assert(!gbinder_remote_reply_copy_to_local(NULL));
g_assert(!gbinder_remote_reply_read_int32(NULL, NULL));
g_assert(!gbinder_remote_reply_read_uint32(NULL, NULL));
g_assert(!gbinder_remote_reply_read_int64(NULL, NULL));
@@ -74,11 +82,11 @@ void
test_empty(
void)
{
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
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);
@@ -120,12 +128,11 @@ test_int32(
};
guint32 out1 = 0;
gint32 out2 = 0;
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
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,
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));
@@ -151,12 +158,11 @@ test_int64(
};
guint64 out1 = 0;
gint64 out2 = 0;
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
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,
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));
@@ -180,12 +186,11 @@ test_string8(
static const guint8 reply_data [] = {
'b', 'a', 'r', 0x00
};
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
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,
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"));
@@ -208,13 +213,12 @@ test_string16(
TEST_INT16_BYTES('b'), TEST_INT16_BYTES('a'),
TEST_INT16_BYTES('r'), 0x00, 0x00
};
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER);
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderRemoteReply* reply = gbinder_remote_reply_new(NULL);
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);
@@ -225,6 +229,55 @@ test_string16(
gbinder_driver_unref(driver);
}
/*==========================================================================*
* to_local
*==========================================================================*/
static
void
test_to_local(
void)
{
static const guint8 reply_data [] = {
/* 32-bit integer */
TEST_INT32_BYTES(42),
/* 64-bit NULL flat_binder_object */
TEST_INT32_BYTES(BINDER_TYPE_BINDER), /* hdr.type */
TEST_INT32_BYTES(0x17f), /* flags */
TEST_INT64_BYTES(0), /* handle */
TEST_INT64_BYTES(0) /* cookie */
};
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
GBinderRemoteReply* req = gbinder_remote_reply_new(NULL);
GBinderLocalReply* req2;
GBinderOutputData* data;
const GByteArray* bytes;
GUtilIntArray* offsets;
guint8* req_data = g_memdup(reply_data, sizeof(reply_data));
void** objects = g_new0(void*, 2);
/* Skip the 32-bit integer */
objects[0] = req_data + 4;
gbinder_remote_reply_set_data(req, gbinder_buffer_new(driver, req_data,
sizeof(reply_data), objects));
/* Convert to GBinderLocalReply */
req2 = gbinder_remote_reply_copy_to_local(req);
data = gbinder_local_reply_data(req2);
offsets = gbinder_output_data_offsets(data);
bytes = data->bytes;
g_assert(offsets);
g_assert(offsets->count == 1);
g_assert(offsets->data[0] == 4);
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(bytes->len == sizeof(reply_data));
gbinder_remote_reply_unref(req);
gbinder_local_reply_unref(req2);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* Common
*==========================================================================*/
@@ -241,6 +294,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_PREFIX "int64", test_int64);
g_test_add_func(TEST_PREFIX "string8", test_string8);
g_test_add_func(TEST_PREFIX "string16", test_string16);
g_test_add_func(TEST_PREFIX "to_local", test_to_local);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -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
@@ -37,6 +37,11 @@
#include "gbinder_reader.h"
#include "gbinder_remote_request_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_request_p.h"
#include "gbinder_output_data.h"
#include "gbinder_io.h"
#include <gutil_intarray.h>
static TestOpt test_opt;
@@ -50,6 +55,9 @@ static TestOpt test_opt;
TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), \
TEST_INT16_BYTES('o'), 0x00, 0x00
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', 0x85)
#define BINDER_OBJECT_SIZE_64 (GBINDER_MAX_BINDER_OBJECT_SIZE)
/*==========================================================================*
* null
*==========================================================================*/
@@ -63,10 +71,13 @@ 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));
g_assert(gbinder_remote_request_sender_pid(NULL) == (pid_t)(-1));
g_assert(gbinder_remote_request_sender_euid(NULL) == (uid_t)(-1));
g_assert(!gbinder_remote_request_read_int32(NULL, NULL));
@@ -91,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));
@@ -109,20 +124,20 @@ void
test_int32(
void)
{
static const guint8 request_data [] = {
static const guint8 req_data [] = {
TEST_RPC_HEADER,
TEST_INT32_BYTES(42)
};
guint32 out1 = 0;
gint32 out2 = 0;
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev);
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
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));
@@ -143,20 +158,20 @@ void
test_int64(
void)
{
static const guint8 request_data [] = {
static const guint8 req_data [] = {
TEST_RPC_HEADER,
TEST_INT64_BYTES(42)
};
guint64 out1 = 0;
gint64 out2 = 0;
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev);
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
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));
@@ -177,18 +192,18 @@ void
test_string8(
void)
{
static const guint8 request_data [] = {
static const guint8 req_data [] = {
TEST_RPC_HEADER,
'b', 'a', 'r', 0x00
};
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev);
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
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"));
@@ -206,21 +221,21 @@ 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'),
TEST_INT16_BYTES('r'), 0x00, 0x00
};
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev);
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
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);
@@ -231,6 +246,59 @@ test_string16(
gbinder_driver_unref(driver);
}
/*==========================================================================*
* to_local
*==========================================================================*/
static
void
test_to_local(
void)
{
static const guint8 request_data [] = {
TEST_RPC_HEADER,
/* 32-bit integer */
TEST_INT32_BYTES(42),
/* 64-bit NULL flat_binder_object */
TEST_INT32_BYTES(BINDER_TYPE_BINDER), /* hdr.type */
TEST_INT32_BYTES(0x17f), /* flags */
TEST_INT64_BYTES(0), /* handle */
TEST_INT64_BYTES(0) /* cookie */
};
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderDriver* driver = gbinder_driver_new(dev, NULL);
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
gbinder_rpc_protocol_for_device(dev), 0, 0);
GBinderLocalRequest* req2;
GBinderOutputData* data;
const GByteArray* bytes;
GUtilIntArray* offsets;
guint8* req_data = g_memdup(request_data, sizeof(request_data));
void** objects = g_new0(void*, 2);
/* Skip the 32-bit integer */
objects[0] = req_data + 4;
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));
/* Convert to GBinderLocalRequest */
req2 = gbinder_remote_request_copy_to_local(req);
data = gbinder_local_request_data(req2);
offsets = gbinder_output_data_offsets(data);
bytes = data->bytes;
g_assert(offsets);
g_assert(offsets->count == 1);
g_assert(offsets->data[0] == 4);
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(bytes->len == sizeof(request_data));
gbinder_remote_request_unref(req);
gbinder_local_request_unref(req2);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* Common
*==========================================================================*/
@@ -246,6 +314,7 @@ int main(int argc, char* argv[])
g_test_add_func(TEST_PREFIX "int64", test_int64);
g_test_add_func(TEST_PREFIX "string8", test_string8);
g_test_add_func(TEST_PREFIX "string16", test_string16);
g_test_add_func(TEST_PREFIX "to_local", test_to_local);
test_init(&test_opt, argc, argv);
return g_test_run();
}

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager
include ../common/Makefile

View File

@@ -0,0 +1,684 @@
/*
* 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_common.h"
#include "gbinder_client_p.h"
#include "gbinder_remote_object_p.h"
#include "gbinder_ipc.h"
#include "gbinder_local_object_p.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include <gutil_strv.h>
#include <gutil_macros.h>
#include <errno.h>
static TestOpt test_opt;
static
void
test_get_service_func(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(FALSE);
}
static
void
test_add_service_func(
GBinderServiceManager* sm,
int status,
void* user_data)
{
g_assert(FALSE);
}
static
void
test_registration_func_inc(
GBinderServiceManager* sm,
const char* name,
void* user_data)
{
int* count = user_data;
(*count)++;
}
static
GBinderLocalReply*
test_transact_func(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
return NULL;
}
/*==========================================================================*
* TestServiceManager
*==========================================================================*/
typedef GBinderServiceManagerClass TestServiceManagerClass;
typedef struct test_servicemanager {
GBinderServiceManager manager;
GBinderRemoteObject* remote;
char** services;
gboolean reject_name;
} TestServiceManager;
#define TEST_SERVICEMANAGER(obj) \
G_CAST(obj, TestServiceManager, manager.parent)
#define TEST_SERVICEMANAGER2(obj, type) \
G_TYPE_CHECK_INSTANCE_CAST((obj), (type), TestServiceManager)
static
char**
test_servicemanager_list(
GBinderServiceManager* sm)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
return g_strdupv(self->services);
}
static
GBinderRemoteObject*
test_servicemanager_get_service(
GBinderServiceManager* sm,
const char* name,
int* status)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
if (gutil_strv_contains(self->services, name)) {
if (!self->remote) {
self->remote = gbinder_ipc_get_remote_object
(gbinder_client_ipc(sm->client), 1);
}
*status = GBINDER_STATUS_OK;
return gbinder_remote_object_ref(self->remote);
} else {
*status = (-ENOENT);
return NULL;
}
}
static
int
test_servicemanager_add_service(
GBinderServiceManager* sm,
const char* name,
GBinderLocalObject* obj)
{
TestServiceManager* self = TEST_SERVICEMANAGER(sm);
if (!gutil_strv_contains(self->services, name)) {
self->services = gutil_strv_add(self->services, name);
}
return GBINDER_STATUS_OK;
}
/*==========================================================================*
* TestHwServiceManager
*==========================================================================*/
typedef TestServiceManagerClass TestHwServiceManagerClass;
typedef TestServiceManager TestHwServiceManager;
G_DEFINE_TYPE(TestHwServiceManager, test_hwservicemanager,
GBINDER_TYPE_SERVICEMANAGER)
#define TEST_HWSERVICEMANAGER_HANDLE (0)
#define TEST_HWSERVICEMANAGER_IFACE "android.hidl.manager@1.0::IServiceManager"
#define TEST_TYPE_HWSERVICEMANAGER (test_hwservicemanager_get_type())
#define TEST_IS_HWSERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_HWSERVICEMANAGER)
#define TEST_HWSERVICEMANAGER(obj) \
TEST_SERVICEMANAGER2(obj, TEST_TYPE_HWSERVICEMANAGER)
static
GBINDER_SERVICEMANAGER_NAME_CHECK
test_hwservicemanager_check_name(
GBinderServiceManager* sm,
const char* name)
{
TestHwServiceManager* self = TEST_HWSERVICEMANAGER(sm);
return (!name || self->reject_name) ?
GBINDER_SERVICEMANAGER_NAME_INVALID :
GBINDER_SERVICEMANAGER_NAME_NORMALIZE;
}
static
char*
test_hwservicemanager_normalize_name(
GBinderServiceManager* self,
const char* name)
{
return g_strdup(name);
}
static
gboolean
test_hwservicemanager_watch(
GBinderServiceManager* manager,
const char* name)
{
return TRUE;
}
static
void
test_hwservicemanager_unwatch(
GBinderServiceManager* manager,
const char* name)
{
}
static
void
test_hwservicemanager_init(
TestHwServiceManager* self)
{
}
static
void
test_hwservicemanager_finalize(
GObject* object)
{
TestHwServiceManager* self = TEST_HWSERVICEMANAGER(object);
gbinder_remote_object_unref(self->remote);
g_strfreev(self->services);
G_OBJECT_CLASS(test_hwservicemanager_parent_class)->finalize(object);
}
static
void
test_hwservicemanager_class_init(
TestHwServiceManagerClass* klass)
{
klass->handle = TEST_HWSERVICEMANAGER_HANDLE;
klass->iface = TEST_HWSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
klass->check_name = test_hwservicemanager_check_name;
klass->normalize_name = test_hwservicemanager_normalize_name;
klass->watch = test_hwservicemanager_watch;
klass->unwatch = test_hwservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = test_hwservicemanager_finalize;
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_HWSERVICEMANAGER,
dev);
}
/*==========================================================================*
* TestDefServiceManager
*==========================================================================*/
typedef TestServiceManagerClass TestDefServiceManagerClass;
typedef TestServiceManager TestDefServiceManager;
G_DEFINE_TYPE(TestDefServiceManager, test_defservicemanager,
GBINDER_TYPE_SERVICEMANAGER)
#define TEST_DEFSERVICEMANAGER_HANDLE (0)
#define TEST_DEFSERVICEMANAGER_IFACE "android.os.IServiceManager"
#define TEST_TYPE_DEFSERVICEMANAGER (test_defservicemanager_get_type())
#define TEST_IS_DEFSERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_DEFSERVICEMANAGER)
#define TEST_DEFSERVICEMANAGER(obj) \
TEST_SERVICEMANAGER2(obj, TEST_TYPE_DEFSERVICEMANAGER)
static
GBINDER_SERVICEMANAGER_NAME_CHECK
test_defservicemanager_check_name(
GBinderServiceManager* sm,
const char* name)
{
TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(sm);
return (!name || self->reject_name) ?
GBINDER_SERVICEMANAGER_NAME_INVALID :
GBINDER_SERVICEMANAGER_NAME_OK;
}
static
gboolean
test_defservicemanager_watch(
GBinderServiceManager* manager,
const char* name)
{
return FALSE;
}
static
void
test_defservicemanager_init(
TestDefServiceManager* self)
{
}
static
void
test_defservicemanager_finalize(
GObject* object)
{
TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(object);
gbinder_remote_object_unref(self->remote);
g_strfreev(self->services);
G_OBJECT_CLASS(test_defservicemanager_parent_class)->finalize(object);
}
static
void
test_defservicemanager_class_init(
TestDefServiceManagerClass* klass)
{
klass->handle = TEST_DEFSERVICEMANAGER_HANDLE;
klass->iface = TEST_DEFSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_BINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
klass->check_name = test_defservicemanager_check_name;
klass->watch = test_defservicemanager_watch;
G_OBJECT_CLASS(klass)->finalize = test_defservicemanager_finalize;
}
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_DEFSERVICEMANAGER,
dev);
}
/*==========================================================================*
* null
*==========================================================================*/
static
void
test_null(
void)
{
g_assert(!gbinder_servicemanager_new_with_type(0, NULL));
g_assert(!gbinder_servicemanager_new_local_object(NULL, NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_ref(NULL));
g_assert(!gbinder_servicemanager_list(NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_list_sync(NULL));
g_assert(!gbinder_servicemanager_get_service(NULL, NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_get_service_sync(NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_add_service(NULL, NULL, NULL, NULL, NULL));
g_assert(gbinder_servicemanager_add_service_sync(NULL, NULL, NULL) ==
(-EINVAL));
g_assert(!gbinder_servicemanager_add_registration_handler(NULL, NULL,
NULL, NULL));
gbinder_servicemanager_remove_handler(NULL, 0);
gbinder_servicemanager_cancel(NULL, 0);
gbinder_servicemanager_unref(NULL);
}
/*==========================================================================*
* invalid
*==========================================================================*/
static
void
test_invalid(
void)
{
int status = 0;
GBinderServiceManager* sm =
gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER);
g_assert(!gbinder_servicemanager_new_with_type(GBINDER_TYPE_LOCAL_OBJECT,
NULL));
g_assert(TEST_IS_HWSERVICEMANAGER(sm));
g_assert(!gbinder_servicemanager_list(sm, NULL, NULL));
g_assert(!gbinder_servicemanager_get_service(sm, "foo", NULL, NULL));
g_assert(!gbinder_servicemanager_get_service(sm, NULL,
test_get_service_func, NULL));
g_assert(!gbinder_servicemanager_get_service_sync(sm, NULL, NULL));
g_assert(!gbinder_servicemanager_get_service_sync(sm, NULL, &status));
g_assert(status == (-EINVAL));
g_assert(!gbinder_servicemanager_add_service(sm, "foo", NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_add_service(sm, NULL, NULL,
test_add_service_func, NULL));
g_assert(gbinder_servicemanager_add_service_sync(sm, NULL, NULL) ==
(-EINVAL));
g_assert(gbinder_servicemanager_add_service_sync(sm, "foo", NULL) ==
(-EINVAL));
g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL, NULL,
NULL));
gbinder_servicemanager_cancel(sm, 0);
gbinder_servicemanager_remove_handler(sm, 0);
gbinder_servicemanager_unref(sm);
}
/*==========================================================================*
* basic
*==========================================================================*/
static
void
test_basic(
void)
{
GBinderServiceManager* sm =
gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER);
GBinderLocalObject* obj;
g_assert(sm);
obj = gbinder_servicemanager_new_local_object(sm, "foo.bar",
test_transact_func, NULL);
g_assert(obj);
gbinder_local_object_unref(obj);
g_assert(gbinder_servicemanager_ref(sm) == sm);
gbinder_servicemanager_unref(sm);
gbinder_servicemanager_unref(sm);
}
/*==========================================================================*
* reuse
*==========================================================================*/
static
void
test_reuse(
void)
{
GBinderServiceManager* m1 =
gbinder_servicemanager_new(GBINDER_DEFAULT_BINDER);
GBinderServiceManager* m2 =
gbinder_servicemanager_new(GBINDER_DEFAULT_BINDER);
GBinderServiceManager* vnd1 =
gbinder_servicemanager_new("/dev/vpnbinder");
GBinderServiceManager* vnd2 =
gbinder_servicemanager_new("/dev/vpnbinder");
GBinderServiceManager* hw1 =
gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER);
GBinderServiceManager* hw2 =
gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER);
g_assert(m1);
g_assert(m1 == m2);
g_assert(vnd1);
g_assert(vnd1 == vnd2);
g_assert(vnd1 != m1);
g_assert(hw1);
g_assert(hw1 == hw2);
g_assert(hw1 != m1);
g_assert(hw1 != vnd1);
gbinder_servicemanager_unref(m1);
gbinder_servicemanager_unref(m2);
gbinder_servicemanager_unref(vnd1);
gbinder_servicemanager_unref(vnd2);
gbinder_servicemanager_unref(hw1);
gbinder_servicemanager_unref(hw2);
}
/*==========================================================================*
* notify
*==========================================================================*/
static
void
test_notify_type(
GType t)
{
GBinderServiceManager* sm = gbinder_servicemanager_new_with_type(t, NULL);
TestHwServiceManager* test = TEST_SERVICEMANAGER2(sm, t);
const char* name = "foo";
int count = 0;
gulong id1 = gbinder_servicemanager_add_registration_handler(sm, name,
test_registration_func_inc, &count);
gulong id2 = gbinder_servicemanager_add_registration_handler(sm, name,
test_registration_func_inc, &count);
g_assert(id1 && id2);
test->services = gutil_strv_add(test->services, name);
gbinder_servicemanager_service_registered(sm, name);
g_assert(count == 2);
count = 0;
/* Nothing is going to happen if the name get rejected by the class */
test->reject_name = TRUE;
g_assert(!gbinder_servicemanager_add_registration_handler(sm, name,
test_registration_func_inc, &count));
gbinder_servicemanager_service_registered(sm, name);
g_assert(!count);
gbinder_servicemanager_remove_handler(sm, id1);
gbinder_servicemanager_remove_handler(sm, id2);
gbinder_servicemanager_unref(sm);
}
static
void
test_notify(
void)
{
test_notify_type(TEST_TYPE_HWSERVICEMANAGER);
test_notify_type(TEST_TYPE_DEFSERVICEMANAGER);
}
/*==========================================================================*
* list
*==========================================================================*/
static
gboolean
test_list_func(
GBinderServiceManager* sm,
char** services,
void* user_data)
{
TestHwServiceManager* test = TEST_SERVICEMANAGER(sm);
g_assert(gutil_strv_equal(test->services, services));
test_quit_later((GMainLoop*)user_data);
return FALSE;
}
static
void
test_list(
void)
{
GBinderServiceManager* sm = gbinder_servicemanager_new(NULL);
TestHwServiceManager* test = TEST_SERVICEMANAGER(sm);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
char** list;
gulong id;
test->services = gutil_strv_add(test->services, "foo");
list = gbinder_servicemanager_list_sync(sm);
g_assert(gutil_strv_equal(test->services, list));
g_strfreev(list);
id = gbinder_servicemanager_list(sm, test_list_func, loop);
g_assert(id);
test_run(&test_opt, loop);
gbinder_servicemanager_unref(sm);
g_main_loop_unref(loop);
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_get_func(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(status == GBINDER_STATUS_OK);
g_assert(obj);
test_quit_later((GMainLoop*)user_data);
}
static
void
test_get(
void)
{
GBinderServiceManager* sm = gbinder_servicemanager_new(NULL);
TestHwServiceManager* test = TEST_SERVICEMANAGER(sm);
int status = -1;
GBinderLocalObject* obj =
gbinder_servicemanager_new_local_object(sm, "foo.bar",
test_transact_func, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id;
/* Add a service */
g_assert(obj);
g_assert(gbinder_servicemanager_add_service_sync(sm, "foo", obj) ==
GBINDER_STATUS_OK);
gbinder_local_object_unref(obj);
g_assert(gutil_strv_contains(test->services, "foo"));
/* And get it back */
g_assert(gbinder_servicemanager_get_service_sync(sm, "foo", &status));
g_assert(status == GBINDER_STATUS_OK);
/* Wrong name */
g_assert(!gbinder_servicemanager_get_service_sync(sm, "bar", &status));
g_assert(status == (-ENOENT));
/* Get it asynchronously */
id = gbinder_servicemanager_get_service(sm, "foo", test_get_func, loop);
g_assert(id);
test_run(&test_opt, loop);
gbinder_servicemanager_unref(sm);
g_main_loop_unref(loop);
}
/*==========================================================================*
* add
*==========================================================================*/
static
void
test_add_func(
GBinderServiceManager* sm,
int status,
void* user_data)
{
g_assert(status == GBINDER_STATUS_OK);
test_quit_later((GMainLoop*)user_data);
}
static
void
test_add(
void)
{
GBinderServiceManager* sm = gbinder_servicemanager_new(NULL);
TestHwServiceManager* test = TEST_SERVICEMANAGER(sm);
GBinderLocalObject* obj =
gbinder_servicemanager_new_local_object(sm, "foo.bar",
test_transact_func, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_servicemanager_add_service(sm, "foo", obj,
test_add_func, loop);
g_assert(id);
test_run(&test_opt, loop);
g_assert(gutil_strv_contains(test->services, "foo"));
gbinder_servicemanager_unref(sm);
g_main_loop_unref(loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager/" 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_("invalid"), test_invalid);
g_test_add_func(TEST_("basic"), test_basic);
g_test_add_func(TEST_("reuse"), test_reuse);
g_test_add_func(TEST_("notify"), test_notify);
g_test_add_func(TEST_("list"), test_list);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("add"), test_add);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicepoll
include ../common/Makefile

View File

@@ -0,0 +1,448 @@
/*
* 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_common.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;
/*==========================================================================*
* 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_HANDLE (0)
#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->handle = TEST_SERVICEMANAGER_HANDLE;
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)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
GBinderServicePoll* 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);
}
/*==========================================================================*
* 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)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* poll;
gulong id;
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);
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)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* poll;
gulong id;
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);
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)
{
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager = gbinder_servicemanager_new(NULL);
GBinderServicePoll* poll = gbinder_servicepoll_new(manager, &weakptr);
TestServiceManager* test = TEST_SERVICEMANAGER(manager);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id;
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);
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:
*/

View File

@@ -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
@@ -39,6 +39,8 @@
#include <gutil_intarray.h>
#include <unistd.h>
static TestOpt test_opt;
#define BUFFER_OBJECT_SIZE_32 (24)
@@ -74,10 +76,13 @@ 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);
gbinder_writer_append_hidl_string(NULL, NULL);
gbinder_writer_append_hidl_string(&writer, NULL);
gbinder_writer_append_hidl_string_vec(NULL, NULL, 0);
@@ -90,9 +95,52 @@ 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);
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);
}
/*==========================================================================*
@@ -201,9 +249,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;
@@ -339,6 +387,136 @@ 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
*==========================================================================*/
typedef struct test_hidl_vec_data {
const char* name;
const GBinderIo* io;
const void* data;
const gsize count;
const gsize elemsize;
const guint* offsets;
guint offsets_count;
guint buffers_size;
} TestHidlVecData;
static guint test_hidl_vec_offsets_0[] =
{ 0 };
static guint test_hidl_vec_offsets_32[] =
{ 0, BUFFER_OBJECT_SIZE_32 };
static guint test_hidl_vec_offsets_64[] =
{ 0, BUFFER_OBJECT_SIZE_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(GBinderHidlVec) },
{ "32/2x1", &gbinder_io_32, "xy", 2, 1,
TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_32),
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(GBinderHidlVec) },
{ "64/2x2", &gbinder_io_64, "xxyy", 2, 2,
TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_64),
sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }
};
static
void
test_hidl_vec(
gconstpointer test_data)
{
const TestHidlVecData* test = test_data;
GBinderLocalRequest* req = gbinder_local_request_new(test->io, NULL);
GBinderOutputData* data;
GBinderWriter writer;
GUtilIntArray* offsets;
guint i;
gbinder_local_request_init_writer(req, &writer);
gbinder_writer_append_hidl_vec(&writer, test->data,
test->count, test->elemsize);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
g_assert(offsets->count == test->offsets_count);
for (i = 0; i < offsets->count; i++) {
g_assert(offsets->data[i] == test->offsets[i]);
}
g_assert(gbinder_output_data_buffers_size(data) == test->buffers_size);
gbinder_local_request_unref(req);
}
/*==========================================================================*
* hidl_string
*==========================================================================*/
@@ -361,15 +539,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
@@ -417,8 +597,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);
}
@@ -449,18 +630,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
@@ -569,6 +750,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
*==========================================================================*/
@@ -621,38 +872,108 @@ 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);
}
/*==========================================================================*
* 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);
}
g_test_add_func(TEST_PREFIX "hidl_string/2strings", test_hidl_string2);
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_("hidl_vec/"), test->name, NULL);
g_test_add_data_func(path, test, test_hidl_vec);
g_free(path);
}
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);
@@ -660,17 +981,20 @@ 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);
test_init(&test_opt, argc, argv);
return g_test_run();
}