Compare commits
	
		
			41 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d9903e7398 | ||
| 
						 | 
					39b69f27ee | ||
| 
						 | 
					31a1d19b4e | ||
| 
						 | 
					f4a923c3dc | ||
| 
						 | 
					171ff7d1e4 | ||
| 
						 | 
					5cfbf22b81 | ||
| 
						 | 
					5a35ed5ea1 | ||
| 
						 | 
					1978359f15 | ||
| 
						 | 
					fef6543f1a | ||
| 
						 | 
					b1a9200803 | ||
| 
						 | 
					fada30868a | ||
| 
						 | 
					8414e9485f | ||
| 
						 | 
					f6d8d485bc | ||
| 
						 | 
					6ea0d6c631 | ||
| 
						 | 
					31c6c05c1e | ||
| 
						 | 
					d855d695db | ||
| 
						 | 
					922cc82029 | ||
| 
						 | 
					80498378f2 | ||
| 
						 | 
					51856865df | ||
| 
						 | 
					d26dcca37e | ||
| 
						 | 
					5ac02fcb2e | ||
| 
						 | 
					110cd65779 | ||
| 
						 | 
					67e665b619 | ||
| 
						 | 
					d1c431c370 | ||
| 
						 | 
					2167a82c73 | ||
| 
						 | 
					d113d3bf4a | ||
| 
						 | 
					b2206adae5 | ||
| 
						 | 
					8075cce1b1 | ||
| 
						 | 
					29e4c79f75 | ||
| 
						 | 
					3b299d3345 | ||
| 
						 | 
					7421fff380 | ||
| 
						 | 
					694aad637b | ||
| 
						 | 
					c8c7222e06 | ||
| 
						 | 
					9c6e31ef41 | ||
| 
						 | 
					6a8d5c0c6e | ||
| 
						 | 
					96ca10396b | ||
| 
						 | 
					61cef824e8 | ||
| 
						 | 
					972517d32d | ||
| 
						 | 
					217a03642a | ||
| 
						 | 
					f14783b8cf | ||
| 
						 | 
					4f75c6e37b | 
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							@@ -24,7 +24,7 @@ all: debug release pkgconfig
 | 
			
		||||
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 0
 | 
			
		||||
VERSION_RELEASE = 8
 | 
			
		||||
VERSION_RELEASE = 17
 | 
			
		||||
 | 
			
		||||
# 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 += \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								README
									
									
									
									
									
								
							@@ -1 +1,7 @@
 | 
			
		||||
GLib-style interface to binder (Android IPC mechanism)
 | 
			
		||||
 | 
			
		||||
Provides:
 | 
			
		||||
 | 
			
		||||
1. Integration with GLib event loop
 | 
			
		||||
2. Detection of 32 vs 64 bit kernel at runtime
 | 
			
		||||
3. Asynchronous transactions that don't block the event thread
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,65 @@
 | 
			
		||||
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()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,13 +2,13 @@ Source: libgbinder
 | 
			
		||||
Section: libs
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Build-Depends: debhelper (>= 7), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.29)
 | 
			
		||||
Build-Depends: debhelper (>= 7), libglib2.0-dev (>= 2.0), libglibutil (>= 1.0.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
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ struct gbinder_reader {
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_at_end(
 | 
			
		||||
    GBinderReader* reader);
 | 
			
		||||
    const GBinderReader* reader);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_reader_read_byte(
 | 
			
		||||
@@ -113,12 +113,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)
 | 
			
		||||
@@ -146,17 +165,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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
@@ -149,6 +155,12 @@ gbinder_writer_append_remote_object(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    GBinderRemoteObject* obj);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_byte_array(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const void* byte_array,
 | 
			
		||||
    gint32 len); /* since 1.0.12 */
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_WRITER_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
Name: libgbinder
 | 
			
		||||
Version: 1.0.8
 | 
			
		||||
Version: 1.0.17
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
@@ -43,13 +44,31 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManager GBinderDefaultServiceManager;
 | 
			
		||||
typedef struct gbinder_defaultservicemanager_watch {
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    char* name;
 | 
			
		||||
    gulong handler_id;
 | 
			
		||||
    guint notify_id;
 | 
			
		||||
} GBinderDefaultServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderDefaultServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_defaultservicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GBinderServicePoll* poll;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderDefaultServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderDefaultServiceManager,
 | 
			
		||||
    gbinder_defaultservicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_defaultservicemanager_parent_class
 | 
			
		||||
#define GBINDER_TYPE_DEFAULTSERVICEMANAGER \
 | 
			
		||||
    gbinder_defaultservicemanager_get_type()
 | 
			
		||||
#define GBINDER_DEFAULTSERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_DEFAULTSERVICEMANAGER, \
 | 
			
		||||
    GBinderDefaultServiceManager)
 | 
			
		||||
 | 
			
		||||
enum gbinder_defaultservicemanager_calls {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
@@ -66,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
 | 
			
		||||
@@ -151,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
 | 
			
		||||
@@ -171,6 +309,11 @@ gbinder_defaultservicemanager_class_init(
 | 
			
		||||
    klass->list = gbinder_defaultservicemanager_list;
 | 
			
		||||
    klass->get_service = gbinder_defaultservicemanager_get_service;
 | 
			
		||||
    klass->add_service = gbinder_defaultservicemanager_add_service;
 | 
			
		||||
    klass->check_name = gbinder_defaultservicemanager_check_name;
 | 
			
		||||
    /* normalize_name is not needed */
 | 
			
		||||
    klass->watch = gbinder_defaultservicemanager_watch;
 | 
			
		||||
    klass->unwatch = gbinder_defaultservicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_defaultservicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -35,20 +35,36 @@
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_object.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
#include <gbinder_remote_request.h>
 | 
			
		||||
#include <gbinder_reader.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManager GBinderHwServiceManager;
 | 
			
		||||
typedef struct gbinder_hwservicemanager_watch {
 | 
			
		||||
    char* name;
 | 
			
		||||
    GBinderLocalObject* callback;
 | 
			
		||||
} GBinderHwServiceManagerWatch;
 | 
			
		||||
 | 
			
		||||
typedef GBinderServiceManagerClass GBinderHwServiceManagerClass;
 | 
			
		||||
typedef struct gbinder_hwservicemanager {
 | 
			
		||||
    GBinderServiceManager manager;
 | 
			
		||||
    GHashTable* watch_table;
 | 
			
		||||
} GBinderHwServiceManager;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderHwServiceManager,
 | 
			
		||||
    gbinder_hwservicemanager,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_hwservicemanager_parent_class
 | 
			
		||||
#define GBINDER_TYPE_HWSERVICEMANAGER (gbinder_hwservicemanager_get_type())
 | 
			
		||||
#define GBINDER_HWSERVICEMANAGER(obj) \
 | 
			
		||||
    G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_HWSERVICEMANAGER, \
 | 
			
		||||
    GBinderHwServiceManager)
 | 
			
		||||
 | 
			
		||||
enum gbinder_hwservicemanager_calls {
 | 
			
		||||
    GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    ADD_TRANSACTION,
 | 
			
		||||
@@ -60,9 +76,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(
 | 
			
		||||
@@ -75,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
 | 
			
		||||
@@ -111,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);
 | 
			
		||||
@@ -170,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
 | 
			
		||||
@@ -190,6 +386,11 @@ gbinder_hwservicemanager_class_init(
 | 
			
		||||
    klass->list = gbinder_hwservicemanager_list;
 | 
			
		||||
    klass->get_service = gbinder_hwservicemanager_get_service;
 | 
			
		||||
    klass->add_service = gbinder_hwservicemanager_add_service;
 | 
			
		||||
    klass->check_name = gbinder_hwservicemanager_check_name;
 | 
			
		||||
    klass->normalize_name = gbinder_hwservicemanager_normalize_name;
 | 
			
		||||
    klass->watch = gbinder_hwservicemanager_watch;
 | 
			
		||||
    klass->unwatch = gbinder_hwservicemanager_unwatch;
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_hwservicemanager_finalize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -128,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
@@ -134,6 +135,9 @@ struct gbinder_ipc_looper {
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderIpc* ipc; /* Not a reference! */
 | 
			
		||||
    GThread* thread;
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    GCond start_cond;
 | 
			
		||||
    gboolean started;
 | 
			
		||||
    int pipefd[2];
 | 
			
		||||
    int txfd[2];
 | 
			
		||||
};
 | 
			
		||||
@@ -344,6 +348,8 @@ gbinder_ipc_looper_free(
 | 
			
		||||
        close(looper->txfd[1]);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_driver_unref(looper->driver);
 | 
			
		||||
    g_cond_clear(&looper->start_cond);
 | 
			
		||||
    g_mutex_clear(&looper->mutex);
 | 
			
		||||
    g_slice_free(GBinderIpcLooper, looper);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -381,6 +387,11 @@ gbinder_ipc_looper_thread(
 | 
			
		||||
        int result;
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Looper %s running", gbinder_driver_dev(driver));
 | 
			
		||||
        g_mutex_lock(&looper->mutex);
 | 
			
		||||
        looper->started = TRUE;
 | 
			
		||||
        g_cond_broadcast(&looper->start_cond);
 | 
			
		||||
        g_mutex_unlock(&looper->mutex);
 | 
			
		||||
 | 
			
		||||
        memset(&pipefd, 0, sizeof(pipefd));
 | 
			
		||||
        pipefd.fd = looper->pipefd[0]; /* read end of the pipe */
 | 
			
		||||
        pipefd.events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
 | 
			
		||||
@@ -436,6 +447,11 @@ gbinder_ipc_looper_thread(
 | 
			
		||||
        } else {
 | 
			
		||||
            GDEBUG("Looper %s is abandoned", gbinder_driver_dev(driver));
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        g_mutex_lock(&looper->mutex);
 | 
			
		||||
        looper->started = TRUE;
 | 
			
		||||
        g_cond_broadcast(&looper->start_cond);
 | 
			
		||||
        g_mutex_unlock(&looper->mutex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_looper_unref(looper);
 | 
			
		||||
@@ -460,6 +476,8 @@ gbinder_ipc_looper_new(
 | 
			
		||||
        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->handler.f = &handler_functions;
 | 
			
		||||
        looper->ipc = ipc;
 | 
			
		||||
        looper->driver = gbinder_driver_ref(ipc->driver);
 | 
			
		||||
@@ -488,6 +506,7 @@ gbinder_ipc_looper_check(
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        if (!priv->looper) {
 | 
			
		||||
            GBinderIpcLooper* looper;
 | 
			
		||||
            /* Lock */
 | 
			
		||||
            g_mutex_lock(&priv->looper_mutex);
 | 
			
		||||
            if (!priv->looper) {
 | 
			
		||||
@@ -496,6 +515,23 @@ gbinder_ipc_looper_check(
 | 
			
		||||
            }
 | 
			
		||||
            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. */
 | 
			
		||||
            looper = priv->looper;
 | 
			
		||||
            if (!looper->started) {
 | 
			
		||||
                /* Lock */
 | 
			
		||||
                g_mutex_lock(&looper->mutex);
 | 
			
		||||
                if (!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(looper->started);
 | 
			
		||||
                }
 | 
			
		||||
                g_mutex_unlock(&looper->mutex);
 | 
			
		||||
                /* Unlock */
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,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 +83,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -305,6 +307,23 @@ 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(
 | 
			
		||||
@@ -316,8 +335,8 @@ gbinder_reader_read_hidl_vec(
 | 
			
		||||
    gsize out_count = 0, out_elemsize = 0;
 | 
			
		||||
    const void* out = NULL;
 | 
			
		||||
 | 
			
		||||
    if (buf && buf->size == sizeof(HidlVec)) {
 | 
			
		||||
        const HidlVec* vec = buf->data;
 | 
			
		||||
    if (buf && buf->size == sizeof(GBinderHidlVec)) {
 | 
			
		||||
        const GBinderHidlVec* vec = buf->data;
 | 
			
		||||
        const void* next = vec->data.ptr;
 | 
			
		||||
 | 
			
		||||
        if (next) {
 | 
			
		||||
@@ -345,6 +364,20 @@ gbinder_reader_read_hidl_vec(
 | 
			
		||||
    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)
 | 
			
		||||
@@ -352,8 +385,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 &&
 | 
			
		||||
@@ -374,8 +407,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;
 | 
			
		||||
 | 
			
		||||
@@ -388,8 +421,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;
 | 
			
		||||
@@ -397,7 +431,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) {
 | 
			
		||||
@@ -458,6 +492,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);
 | 
			
		||||
 | 
			
		||||
@@ -471,15 +523,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -524,24 +582,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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, klass->rpc_protocol);
 | 
			
		||||
        if (ipc) {
 | 
			
		||||
            GBinderRemoteObject* object = gbinder_ipc_get_remote_object
 | 
			
		||||
                (ipc, klass->handle);
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicemanager_watch_free(
 | 
			
		||||
    gpointer data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerWatch* watch = data;
 | 
			
		||||
 | 
			
		||||
            if (object) {
 | 
			
		||||
                /* Lock */
 | 
			
		||||
                g_mutex_lock(&klass->mutex);
 | 
			
		||||
                if (klass->table) {
 | 
			
		||||
                    self = g_hash_table_lookup(klass->table, dev);
 | 
			
		||||
                }
 | 
			
		||||
                if (self) {
 | 
			
		||||
                    gbinder_servicemanager_ref(self);
 | 
			
		||||
                } else {
 | 
			
		||||
                    char* key = g_strdup(dev); /* Owned by the hashtable */
 | 
			
		||||
 | 
			
		||||
                    GVERBOSE_("%s", dev);
 | 
			
		||||
                    self = g_object_new(type, NULL);
 | 
			
		||||
                    self->client = gbinder_client_new(object, klass->iface);
 | 
			
		||||
                    self->dev = gbinder_remote_object_dev(object);
 | 
			
		||||
                    if (!klass->table) {
 | 
			
		||||
                        klass->table = g_hash_table_new_full(g_str_hash,
 | 
			
		||||
                            g_str_equal, g_free, NULL);
 | 
			
		||||
                    }
 | 
			
		||||
                    g_hash_table_replace(klass->table, key, self);
 | 
			
		||||
                }
 | 
			
		||||
                g_mutex_unlock(&klass->mutex);
 | 
			
		||||
                /* Unlock */
 | 
			
		||||
                gbinder_remote_object_unref(object);
 | 
			
		||||
            }
 | 
			
		||||
            gbinder_ipc_unref(ipc);
 | 
			
		||||
        }
 | 
			
		||||
        g_type_class_unref(klass);
 | 
			
		||||
    }
 | 
			
		||||
    return self;
 | 
			
		||||
    g_free(watch->name);
 | 
			
		||||
    g_free(watch->detail);
 | 
			
		||||
    g_free(watch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_servicemanager_list_tx_data {
 | 
			
		||||
@@ -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 */
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -63,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);
 | 
			
		||||
@@ -73,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
									
								
							
							
						
						
									
										268
									
								
								src/gbinder_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_servicemanager.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
/* This is configurable mostly so that unit testing doesn't take too long */
 | 
			
		||||
guint gbinder_servicepoll_interval_ms = 2000;
 | 
			
		||||
 | 
			
		||||
typedef GObjectClass GBinderServicePollClass;
 | 
			
		||||
struct gbinder_servicepoll {
 | 
			
		||||
    GObject object;
 | 
			
		||||
    GBinderServiceManager* manager;
 | 
			
		||||
    char** list;
 | 
			
		||||
    gulong list_id;
 | 
			
		||||
    guint timer_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(GBinderServicePoll, gbinder_servicepoll, G_TYPE_OBJECT)
 | 
			
		||||
#define GBINDER_TYPE_SERVICEPOLL (gbinder_servicepoll_get_type())
 | 
			
		||||
#define GBINDER_SERVICEPOLL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_SERVICEPOLL, GBinderServicePoll))
 | 
			
		||||
 | 
			
		||||
enum gbinder_servicepoll_signal {
 | 
			
		||||
    SIGNAL_NAME_ADDED,
 | 
			
		||||
    SIGNAL_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char SIGNAL_NAME_ADDED_NAME[] = "servicepoll-name-added";
 | 
			
		||||
 | 
			
		||||
static guint gbinder_servicepoll_signals[SIGNAL_COUNT] = { 0 };
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Implementation
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
/* GBinderServiceManagerListFunc callback returns TRUE to keep the services
 | 
			
		||||
 * list, otherwise the caller will deallocate it. */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_list(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicepoll_ref(self);
 | 
			
		||||
    self->list_id = 0;
 | 
			
		||||
    if (services) {
 | 
			
		||||
        const GStrV* ptr_new;
 | 
			
		||||
 | 
			
		||||
        ptr_new = services = gutil_strv_sort(services, TRUE);
 | 
			
		||||
        if (self->list) {
 | 
			
		||||
            const GStrV* ptr_old = self->list;
 | 
			
		||||
 | 
			
		||||
            while (*ptr_new && *ptr_old) {
 | 
			
		||||
                const int i = gutil_strv_find(ptr_old, *ptr_new);
 | 
			
		||||
 | 
			
		||||
                if (i < 0) {
 | 
			
		||||
                    /* New name */
 | 
			
		||||
                    g_signal_emit(self, gbinder_servicepoll_signals
 | 
			
		||||
                        [SIGNAL_NAME_ADDED], 0, *ptr_new);
 | 
			
		||||
                } else {
 | 
			
		||||
                    int k;
 | 
			
		||||
 | 
			
		||||
                    /* If some names have disappeared, then i may be > 0 */
 | 
			
		||||
                    for (k = 0; k < i; k ++) ptr_old++;
 | 
			
		||||
                    ptr_old++;
 | 
			
		||||
                }
 | 
			
		||||
                ptr_new++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        while (*ptr_new) {
 | 
			
		||||
            g_signal_emit(self, gbinder_servicepoll_signals
 | 
			
		||||
                [SIGNAL_NAME_ADDED], 0, *ptr_new);
 | 
			
		||||
            ptr_new++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_strfreev(self->list);
 | 
			
		||||
    self->list = services;
 | 
			
		||||
    gbinder_servicepoll_unref(self);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_timer(
 | 
			
		||||
    gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(user_data);
 | 
			
		||||
 | 
			
		||||
    if (!self->list_id) {
 | 
			
		||||
        self->list_id = gbinder_servicemanager_list(self->manager,
 | 
			
		||||
            gbinder_servicepoll_list, self);
 | 
			
		||||
    }
 | 
			
		||||
    return G_SOURCE_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_create(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = g_object_new(GBINDER_TYPE_SERVICEPOLL, NULL);
 | 
			
		||||
 | 
			
		||||
    self->manager = gbinder_servicemanager_ref(manager);
 | 
			
		||||
    self->list_id = gbinder_servicemanager_list(manager,
 | 
			
		||||
        gbinder_servicepoll_list, self);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * API
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_new(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    GBinderServicePoll** weakptr)
 | 
			
		||||
{
 | 
			
		||||
    if (weakptr) {
 | 
			
		||||
        if (*weakptr) {
 | 
			
		||||
            gbinder_servicepoll_ref(*weakptr);
 | 
			
		||||
        } else {
 | 
			
		||||
            *weakptr = gbinder_servicepoll_create(manager);
 | 
			
		||||
            g_object_add_weak_pointer(G_OBJECT(*weakptr), (gpointer*)weakptr);
 | 
			
		||||
        }
 | 
			
		||||
        return *weakptr;
 | 
			
		||||
    } else {
 | 
			
		||||
        return gbinder_servicepoll_create(manager);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_ref(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_ref(GBINDER_SERVICEPOLL(self));
 | 
			
		||||
        return self;
 | 
			
		||||
    } else {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_unref(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        g_object_unref(GBINDER_SERVICEPOLL(self));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicepoll_manager(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) ? self->manager : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_is_known_name(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return G_LIKELY(self) && gutil_strv_contains(self->list, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicepoll_add_handler(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    GBinderServicePollFunc fn,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
 | 
			
		||||
        SIGNAL_NAME_ADDED_NAME, G_CALLBACK(fn), user_data) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_remove_handler(
 | 
			
		||||
    GBinderServicePoll* self,
 | 
			
		||||
    gulong id)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && G_LIKELY(id)) {
 | 
			
		||||
        g_signal_handler_disconnect(self, id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_init(
 | 
			
		||||
    GBinderServicePoll* self)
 | 
			
		||||
{
 | 
			
		||||
    self->timer_id = g_timeout_add(gbinder_servicepoll_interval_ms,
 | 
			
		||||
        gbinder_servicepoll_timer, self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    GBinderServicePoll* self = GBINDER_SERVICEPOLL(object);
 | 
			
		||||
 | 
			
		||||
    g_source_remove(self->timer_id);
 | 
			
		||||
    gbinder_servicemanager_cancel(self->manager, self->list_id);
 | 
			
		||||
    gbinder_servicemanager_unref(self->manager);
 | 
			
		||||
    g_strfreev(self->list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_class_init(
 | 
			
		||||
    GBinderServicePollClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    G_OBJECT_CLASS(klass)->finalize = gbinder_servicepoll_finalize;
 | 
			
		||||
    gbinder_servicepoll_signals[SIGNAL_NAME_ADDED] =
 | 
			
		||||
        g_signal_new(SIGNAL_NAME_ADDED_NAME, G_OBJECT_CLASS_TYPE(klass),
 | 
			
		||||
            G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE,
 | 
			
		||||
            1, G_TYPE_STRING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										88
									
								
								src/gbinder_servicepoll.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/gbinder_servicepoll.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef GBINDER_SERVICEPOLL_H
 | 
			
		||||
#define GBINDER_SERVICEPOLL_H
 | 
			
		||||
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
extern guint gbinder_servicepoll_interval_ms;
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
(*GBinderServicePollFunc)(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name_added,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_new(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    GBinderServicePoll** weakptr);
 | 
			
		||||
 | 
			
		||||
GBinderServicePoll*
 | 
			
		||||
gbinder_servicepoll_ref(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_unref(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicepoll_manager(
 | 
			
		||||
    GBinderServicePoll* poll);
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_servicepoll_is_known_name(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicepoll_add_handler(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    GBinderServicePollFunc func,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_servicepoll_remove_handler(
 | 
			
		||||
    GBinderServicePoll* poll,
 | 
			
		||||
    gulong id);
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_SERVICEPOLL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -44,28 +44,7 @@ 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;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_INLINE_FUNC static inline
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -119,16 +119,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 */
 | 
			
		||||
@@ -165,7 +165,7 @@ gbinder_writer_data_append_bool(
 | 
			
		||||
 | 
			
		||||
    /* Boolean values are padded to 4-byte boundary */
 | 
			
		||||
    padded[0] = (value != FALSE);
 | 
			
		||||
    padded[1] = padded[2] = padded[3] = 0xff;
 | 
			
		||||
    padded[1] = padded[2] = padded[3] = 0;
 | 
			
		||||
    g_byte_array_append(data->bytes, padded, sizeof(padded));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -186,10 +186,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -210,10 +211,11 @@ gbinder_writer_data_append_int64(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    guint64 value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    guint64* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -234,10 +236,11 @@ gbinder_writer_data_append_float(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    gfloat value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    gfloat* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -258,10 +261,11 @@ gbinder_writer_data_append_double(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    gdouble value)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    gdouble* ptr;
 | 
			
		||||
 | 
			
		||||
    g_byte_array_set_size(data->bytes, data->bytes->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(data->bytes->data + (data->bytes->len - sizeof(*ptr)));
 | 
			
		||||
    g_byte_array_set_size(buf, buf->len + sizeof(*ptr));
 | 
			
		||||
    ptr = (void*)(buf->data + (buf->len - sizeof(*ptr)));
 | 
			
		||||
    *ptr = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -301,15 +305,16 @@ gbinder_writer_data_append_string8_len(
 | 
			
		||||
    gsize len)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(str)) {
 | 
			
		||||
        const gsize old_size = data->bytes->len;
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        gsize padded_len = G_ALIGN4(len + 1);
 | 
			
		||||
        guint32* dest;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + padded_len);
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len);
 | 
			
		||||
 | 
			
		||||
        /* Zero the last word */
 | 
			
		||||
        dest = (guint32*)(data->bytes->data + old_size);
 | 
			
		||||
        dest = (guint32*)(buf->data + old_size);
 | 
			
		||||
        dest[padded_len/4 - 1] = 0;
 | 
			
		||||
 | 
			
		||||
        /* Copy the data */
 | 
			
		||||
@@ -346,14 +351,36 @@ gbinder_writer_data_append_string16(
 | 
			
		||||
    gbinder_writer_data_append_string16_len(data, utf8, utf8? strlen(utf8) : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_null(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    /* NULL string */
 | 
			
		||||
    gbinder_writer_data_append_int32(data, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_empty(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const gsize old_size = buf->len;
 | 
			
		||||
    guint16* ptr16;
 | 
			
		||||
 | 
			
		||||
    /* Empty string */
 | 
			
		||||
    g_byte_array_set_size(buf, old_size + 8);
 | 
			
		||||
    ptr16 = (guint16*)(buf->data + old_size);
 | 
			
		||||
    ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_len(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const char* utf8,
 | 
			
		||||
    gssize num_bytes)
 | 
			
		||||
{
 | 
			
		||||
    const gsize old_size = data->bytes->len;
 | 
			
		||||
 | 
			
		||||
    if (utf8) {
 | 
			
		||||
        const char* end = utf8;
 | 
			
		||||
 | 
			
		||||
@@ -364,14 +391,16 @@ gbinder_writer_data_append_string16_len(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (num_bytes > 0) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        glong len = g_utf8_strlen(utf8, num_bytes);
 | 
			
		||||
        gsize padded_len = G_ALIGN4((len+1)*2);
 | 
			
		||||
        guint32* len_ptr;
 | 
			
		||||
        gunichar2* utf16_ptr;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(data->bytes, old_size + padded_len + 4);
 | 
			
		||||
        len_ptr = (guint32*)(data->bytes->data + old_size);
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len + 4);
 | 
			
		||||
        len_ptr = (guint32*)(buf->data + old_size);
 | 
			
		||||
        utf16_ptr = (gunichar2*)(len_ptr + 1);
 | 
			
		||||
 | 
			
		||||
        /* TODO: this could be optimized for ASCII strings, i.e. if
 | 
			
		||||
@@ -398,17 +427,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -498,13 +582,13 @@ gbinder_writer_data_append_hidl_vec(
 | 
			
		||||
    guint elemsize)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent vec_parent;
 | 
			
		||||
    HidlVec* vec = g_new0(HidlVec, 1);
 | 
			
		||||
    GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
 | 
			
		||||
    const gsize total = count * elemsize;
 | 
			
		||||
    void* buf = g_memdup(base, total);
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the string data */
 | 
			
		||||
    vec_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    vec_parent.offset = HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
    vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the vector descriptor */
 | 
			
		||||
    if (buf) {
 | 
			
		||||
@@ -544,12 +628,12 @@ gbinder_writer_data_append_hidl_string(
 | 
			
		||||
    const char* str)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent str_parent;
 | 
			
		||||
    HidlString* hidl_string = g_new0(HidlString, 1);
 | 
			
		||||
    GBinderHidlString* hidl_string = g_new0(GBinderHidlString, 1);
 | 
			
		||||
    const gsize len = str ? strlen(str) : 0;
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the string data */
 | 
			
		||||
    str_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    str_parent.offset = HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
    str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the string descriptor and store it */
 | 
			
		||||
    hidl_string->data.str = str;
 | 
			
		||||
@@ -591,8 +675,8 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
    gssize count)
 | 
			
		||||
{
 | 
			
		||||
    GBinderParent vec_parent;
 | 
			
		||||
    HidlVec* vec = g_new0(HidlVec, 1);
 | 
			
		||||
    HidlString* strings = NULL;
 | 
			
		||||
    GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1);
 | 
			
		||||
    GBinderHidlString* strings = NULL;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (count < 0) {
 | 
			
		||||
@@ -602,11 +686,11 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
    /* Prepare parent descriptor for the vector data */
 | 
			
		||||
    vec_parent.index = gbinder_writer_data_prepare(data);
 | 
			
		||||
    vec_parent.offset = HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
    vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
    /* Fill in the vector descriptor */
 | 
			
		||||
    if (count > 0) {
 | 
			
		||||
        strings = g_new0(HidlString, count);
 | 
			
		||||
        strings = g_new0(GBinderHidlString, count);
 | 
			
		||||
        vec->data.ptr = strings;
 | 
			
		||||
        data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, strings);
 | 
			
		||||
    }
 | 
			
		||||
@@ -617,7 +701,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
    /* Fill in string descriptors */
 | 
			
		||||
    for (i = 0; i < count; i++) {
 | 
			
		||||
        const char* str = strv[i];
 | 
			
		||||
        HidlString* hidl_str = strings + i;
 | 
			
		||||
        GBinderHidlString* hidl_str = strings + i;
 | 
			
		||||
 | 
			
		||||
        if ((hidl_str->data.str = str) != NULL) {
 | 
			
		||||
            hidl_str->len = strlen(str);
 | 
			
		||||
@@ -632,7 +716,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
        /* Prepare parent descriptor for the string data */
 | 
			
		||||
        str_parent.index = data->offsets->count;
 | 
			
		||||
        str_parent.offset = HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
        str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET;
 | 
			
		||||
 | 
			
		||||
        /* Write the vector data (it's parent for the string data) */
 | 
			
		||||
        gbinder_writer_data_write_buffer_object(data, strings,
 | 
			
		||||
@@ -640,7 +724,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
 | 
			
		||||
        /* Write the string data */
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
            HidlString* hidl_str = strings + i;
 | 
			
		||||
            GBinderHidlString* hidl_str = strings + i;
 | 
			
		||||
 | 
			
		||||
            if (hidl_str->data.str) {
 | 
			
		||||
                gbinder_writer_data_write_buffer_object(data,
 | 
			
		||||
@@ -649,7 +733,7 @@ gbinder_writer_data_append_hidl_string_vec(
 | 
			
		||||
                    (guint)hidl_str->len, (guint)str_parent.index,
 | 
			
		||||
                    (guint)data->buffers_size);
 | 
			
		||||
            }
 | 
			
		||||
            str_parent.offset += sizeof(HidlString);
 | 
			
		||||
            str_parent.offset += sizeof(GBinderHidlString);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -671,16 +755,16 @@ gbinder_writer_data_append_local_object(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* dest = data->bytes;
 | 
			
		||||
    const guint offset = dest->len;
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const guint offset = buf->len;
 | 
			
		||||
    guint n;
 | 
			
		||||
 | 
			
		||||
    /* Preallocate enough space */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE);
 | 
			
		||||
    /* Write the object */
 | 
			
		||||
    n = data->io->encode_local_object(dest->data + offset, obj);
 | 
			
		||||
    n = data->io->encode_local_object(buf->data + offset, obj);
 | 
			
		||||
    /* Fix the data size */
 | 
			
		||||
    g_byte_array_set_size(dest, offset + n);
 | 
			
		||||
    g_byte_array_set_size(buf, offset + n);
 | 
			
		||||
    /* Record the offset */
 | 
			
		||||
    gbinder_writer_data_record_offset(data, offset);
 | 
			
		||||
}
 | 
			
		||||
@@ -697,21 +781,51 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,8 @@ unit_reader \
 | 
			
		||||
unit_remote_object \
 | 
			
		||||
unit_remote_reply \
 | 
			
		||||
unit_remote_request \
 | 
			
		||||
unit_servicemanager \
 | 
			
		||||
unit_servicepoll \
 | 
			
		||||
unit_writer"
 | 
			
		||||
 | 
			
		||||
function err() {
 | 
			
		||||
 
 | 
			
		||||
@@ -186,6 +186,48 @@ 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, 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,9 +237,7 @@ 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);
 | 
			
		||||
@@ -627,6 +667,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -137,8 +137,8 @@ void
 | 
			
		||||
test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0xff, 0xff, 0xff };
 | 
			
		||||
    static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 };
 | 
			
		||||
    static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 };
 | 
			
		||||
    GBinderLocalReply* reply = gbinder_local_reply_new(&gbinder_io_32);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
 | 
			
		||||
@@ -359,7 +359,7 @@ test_hidl_string(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlString));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
@@ -382,7 +382,7 @@ test_hidl_string_vec(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlVec));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -167,8 +167,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;
 | 
			
		||||
 | 
			
		||||
@@ -375,7 +375,7 @@ test_hidl_string(
 | 
			
		||||
    offsets = gbinder_output_data_offsets(data);
 | 
			
		||||
    g_assert(offsets->count == 1);
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == sizeof(HidlString));
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
 | 
			
		||||
    g_assert(data->bytes->len == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
@@ -398,7 +398,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);
 | 
			
		||||
}
 | 
			
		||||
@@ -525,8 +525,9 @@ test_remote_request_obj_validate_data(
 | 
			
		||||
    g_assert(offsets->data[1] == 4 + BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
    g_assert(offsets->data[2] == 4 + 2*BUFFER_OBJECT_SIZE_64);
 | 
			
		||||
    g_assert(bytes->len == 4 + 2*BUFFER_OBJECT_SIZE_64 + BINDER_OBJECT_SIZE_64);
 | 
			
		||||
    /* HidlString + the contents (2 bytes) aligned at 8-byte boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == (sizeof(HidlString)+8));
 | 
			
		||||
    /* GBinderHidlString + the contents (2 bytes) aligned at 8-byte boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) ==
 | 
			
		||||
        (sizeof(GBinderHidlString) + 8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,7 @@ test_empty(
 | 
			
		||||
{
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    gsize count = 1, elemsize = 1;
 | 
			
		||||
    gsize len;
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, NULL, 0, 0);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
@@ -86,8 +87,11 @@ test_empty(
 | 
			
		||||
    g_assert(!gbinder_reader_read_object(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_nullable_object(&reader, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_buffer(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_struct1(&reader, 1));
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_vec(&reader, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_vec(&reader, &count, &elemsize));
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_vec1(&reader, NULL, 1));
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_vec1(&reader, &count, 1));
 | 
			
		||||
    g_assert(!count);
 | 
			
		||||
    g_assert(!elemsize);
 | 
			
		||||
    g_assert(!gbinder_reader_read_hidl_string(&reader));
 | 
			
		||||
@@ -96,6 +100,7 @@ test_empty(
 | 
			
		||||
    g_assert(!gbinder_reader_read_string8(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_string16(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_skip_string16(&reader));
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte_array(&reader, &len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -440,6 +445,8 @@ test_string16_null(
 | 
			
		||||
    GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
    gunichar2* out2 = NULL;
 | 
			
		||||
    gsize len = 0;
 | 
			
		||||
    char dummy;
 | 
			
		||||
    char* out = &dummy;
 | 
			
		||||
 | 
			
		||||
@@ -449,6 +456,16 @@ test_string16_null(
 | 
			
		||||
        g_memdup(TEST_ARRAY_AND_SIZE(test_string16_in_null)),
 | 
			
		||||
        sizeof(test_string16_in_null));
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null));
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, NULL, NULL));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null));
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, &out2, &len));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(!out2);
 | 
			
		||||
    g_assert(!len);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null));
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16(&reader, NULL));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
@@ -480,6 +497,8 @@ test_string16(
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
    const gboolean valid = (test->out != NULL);
 | 
			
		||||
    gunichar2* out2 = NULL;
 | 
			
		||||
    gsize len = 0;
 | 
			
		||||
    char* str = NULL;
 | 
			
		||||
 | 
			
		||||
    g_assert(driver);
 | 
			
		||||
@@ -487,6 +506,22 @@ test_string16(
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, g_memdup(test->in, test->in_size),
 | 
			
		||||
        test->in_size);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, NULL,
 | 
			
		||||
        NULL) == valid);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader) == (!test->remaining));
 | 
			
		||||
    g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining);
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, &out2,
 | 
			
		||||
        &len) == valid);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader) == (!test->remaining));
 | 
			
		||||
    g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining);
 | 
			
		||||
    if (valid) {
 | 
			
		||||
        g_assert(out2);
 | 
			
		||||
        g_assert((gsize)len == strlen(test->out));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
    g_assert(gbinder_reader_read_nullable_string16(&reader, NULL) == valid);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader) == (!test->remaining));
 | 
			
		||||
@@ -515,6 +550,72 @@ test_string16(
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_struct
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
typedef struct test_hidl_struct {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const void* in;
 | 
			
		||||
    guint in_size;
 | 
			
		||||
    guint struct_size;
 | 
			
		||||
    const void* data;
 | 
			
		||||
} TestHidlStruct;
 | 
			
		||||
 | 
			
		||||
typedef struct test_hidl_struct_type {
 | 
			
		||||
    guint32 x;
 | 
			
		||||
} TestHidlStructType;
 | 
			
		||||
 | 
			
		||||
static const TestHidlStructType test_hidl_struct_data = { 0 };
 | 
			
		||||
static const BinderObject64 test_hidl_struct_ok_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_struct_data },
 | 
			
		||||
        sizeof(test_hidl_struct_data), 0, 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
static const BinderObject64 test_hidl_struct_big_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_struct_data },
 | 
			
		||||
        2 * sizeof(test_hidl_struct_data), 0, 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const TestHidlStruct test_hidl_struct_tests[] = {
 | 
			
		||||
    { "ok", TEST_ARRAY_AND_SIZE(test_hidl_struct_ok_buf),
 | 
			
		||||
      sizeof(TestHidlStructType), &test_hidl_struct_data },
 | 
			
		||||
    { "badsize",  TEST_ARRAY_AND_SIZE(test_hidl_struct_big_buf),
 | 
			
		||||
      sizeof(TestHidlStructType), NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_hidl_struct(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestHidlStruct* test = test_data;
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
			
		||||
    GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
 | 
			
		||||
        g_memdup(test->in, test->in_size), test->in_size);
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
 | 
			
		||||
    g_assert(ipc);
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = buf;
 | 
			
		||||
    data.reg = gbinder_ipc_object_registry(ipc);
 | 
			
		||||
    data.objects = g_new0(void*, 2);
 | 
			
		||||
    data.objects[0] = buf->data;
 | 
			
		||||
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
    g_assert(gbinder_reader_read_hidl_struct1(&reader, test->struct_size) ==
 | 
			
		||||
        test->data);
 | 
			
		||||
 | 
			
		||||
    g_free(data.objects);
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_vec
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -532,7 +633,7 @@ typedef struct test_hidl_vec {
 | 
			
		||||
 | 
			
		||||
static const guint test_hidl_vec_2offsets [] = { 0, BUFFER_OBJECT_SIZE_64 };
 | 
			
		||||
static const guint8 test_hidl_vec_2bytes_data [] = { 0x01, 0x02 };
 | 
			
		||||
static const HidlVec test_hidl_vec_2bytes = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_2bytes = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_2bytes_data,
 | 
			
		||||
    sizeof(test_hidl_vec_2bytes_data),
 | 
			
		||||
    TRUE
 | 
			
		||||
@@ -541,79 +642,81 @@ static const BinderObject64 test_hidl_vec_2bytes_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_2bytes },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_2bytes_data },
 | 
			
		||||
        sizeof(test_hidl_vec_2bytes_data), 0, 0
 | 
			
		||||
        sizeof(test_hidl_vec_2bytes_data), 0,
 | 
			
		||||
        GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const HidlVec test_hidl_vec_empty = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_empty = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_2bytes_data, 0, TRUE
 | 
			
		||||
};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_empty_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_empty },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_2bytes_data },
 | 
			
		||||
        0, 0, 0
 | 
			
		||||
        0, 0, GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint test_hidl_vec_1offset [] = {0};
 | 
			
		||||
static const HidlVec test_hidl_vec_null = {{0}, 0, TRUE};
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_null = {{0}, 0, TRUE};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_null_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_null },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Buffer smaller than HidlVec */
 | 
			
		||||
/* Buffer smaller than GBinderHidlVec */
 | 
			
		||||
static const BinderObject64 test_hidl_vec_short_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_empty },
 | 
			
		||||
        sizeof(HidlVec) - 1, 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec) - 1, 0, 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* NULL buffer with size 1 */
 | 
			
		||||
static const guint test_hidl_vec_badnull_offsets [] = {0};
 | 
			
		||||
static const HidlVec test_hidl_vec_badnull = {{0}, 1, TRUE};
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_badnull = {{0}, 1, TRUE};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_badnull_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_badnull },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Buffer size not divisible by count */
 | 
			
		||||
static const guint8 test_hidl_vec_badsize_data [] = { 0x01, 0x02, 0x03 };
 | 
			
		||||
static const HidlVec test_hidl_vec_badsize = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_badsize = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_badsize_data, 2, TRUE
 | 
			
		||||
};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_badsize_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_badsize },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_badsize_data },
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0, 0
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0,
 | 
			
		||||
        GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Bad buffer address */
 | 
			
		||||
static const guint8 test_hidl_vec_badbuf_data [] = { 0x01, 0x02, 0x03 };
 | 
			
		||||
static const HidlVec test_hidl_vec_badbuf = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_badbuf = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_badbuf_data,
 | 
			
		||||
    sizeof(test_hidl_vec_badbuf_data), TRUE
 | 
			
		||||
};
 | 
			
		||||
@@ -621,43 +724,45 @@ static const BinderObject64 test_hidl_vec_badbuf_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_badbuf },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_badsize_data },
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0, 0
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0,
 | 
			
		||||
        GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Non-zero count and zero size */
 | 
			
		||||
static const HidlVec test_hidl_vec_badcount1 = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_badcount1 = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_badsize_data, 1, TRUE
 | 
			
		||||
};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_badcount1_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_badcount1 },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_badsize_data },
 | 
			
		||||
        0, 0, 0
 | 
			
		||||
        { test_hidl_vec_badsize_data }, 0, 0,
 | 
			
		||||
        GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Zero count0 and non-zero size */
 | 
			
		||||
static const HidlVec test_hidl_vec_badcount2 = {
 | 
			
		||||
static const GBinderHidlVec test_hidl_vec_badcount2 = {
 | 
			
		||||
    .data.ptr = test_hidl_vec_badsize_data, 0, TRUE
 | 
			
		||||
};
 | 
			
		||||
static const BinderObject64 test_hidl_vec_badcount2_buf [] = {
 | 
			
		||||
    {
 | 
			
		||||
        BINDER_TYPE_PTR, 0,
 | 
			
		||||
        { &test_hidl_vec_badcount2 },
 | 
			
		||||
        sizeof(HidlVec), 0, 0
 | 
			
		||||
        sizeof(GBinderHidlVec), 0, 0
 | 
			
		||||
    },{
 | 
			
		||||
        BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT,
 | 
			
		||||
        { test_hidl_vec_badsize_data },
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0, 0
 | 
			
		||||
        sizeof(test_hidl_vec_badsize_data), 0,
 | 
			
		||||
        GBINDER_HIDL_VEC_BUFFER_OFFSET
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -720,6 +825,28 @@ test_hidl_vec(
 | 
			
		||||
    g_assert(n == test->count);
 | 
			
		||||
    g_assert(elem == test->elemsize);
 | 
			
		||||
 | 
			
		||||
    if (test->data) {
 | 
			
		||||
        n = 42;
 | 
			
		||||
        gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
        g_assert(gbinder_reader_read_hidl_vec1(&reader, &n, test->elemsize) ==
 | 
			
		||||
            test->data);
 | 
			
		||||
        g_assert(n == test->count);
 | 
			
		||||
 | 
			
		||||
        /* Test invalid expected size */
 | 
			
		||||
        gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
        if (test->count) {
 | 
			
		||||
            g_assert(!gbinder_reader_read_hidl_vec1(&reader, NULL,
 | 
			
		||||
                test->elemsize + 1));
 | 
			
		||||
        } else {
 | 
			
		||||
            /* If total size is zero, we can't really check the element size */
 | 
			
		||||
            g_assert(gbinder_reader_read_hidl_vec1(&reader, NULL,
 | 
			
		||||
                test->elemsize + 1) == test->data);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_reader_init(&reader, &data, 0, test->in_size);
 | 
			
		||||
        g_assert(!gbinder_reader_read_hidl_vec1(&reader, &n, test->elemsize));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_free(data.objects);
 | 
			
		||||
    gbinder_buffer_free(buf);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
@@ -738,6 +865,14 @@ typedef struct test_hidl_string_err {
 | 
			
		||||
} TestHidlStringErr;
 | 
			
		||||
 | 
			
		||||
static const guint8 test_hidl_string_err_short [] = { 0x00 };
 | 
			
		||||
static const guint8 test_hidl_string_err_bad_obj [] = {
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_TYPE_HANDLE),
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
static const guint8 test_hidl_string_err_empty [] = {
 | 
			
		||||
    TEST_INT32_BYTES(BINDER_TYPE_PTR),
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
@@ -752,6 +887,7 @@ static const guint test_hidl_string_err_one_offset [] = { 0 };
 | 
			
		||||
 | 
			
		||||
static const TestHidlStringErr test_hidl_string_err_tests [] = {
 | 
			
		||||
    { "no-data", TEST_ARRAY_AND_SIZE(test_hidl_string_err_short), NULL },
 | 
			
		||||
    { "no-object", TEST_ARRAY_AND_SIZE(test_hidl_string_err_bad_obj), NULL },
 | 
			
		||||
    { "no-offset", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty), NULL },
 | 
			
		||||
    { "empty-offset", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty),
 | 
			
		||||
        test_hidl_string_err_one_offset, 0 },
 | 
			
		||||
@@ -907,7 +1043,7 @@ test_vec(
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    BinderObject64 obj;
 | 
			
		||||
    HidlVec vec;
 | 
			
		||||
    GBinderHidlVec vec;
 | 
			
		||||
    char** out;
 | 
			
		||||
 | 
			
		||||
    g_assert(ipc);
 | 
			
		||||
@@ -945,45 +1081,204 @@ test_vec(
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * byte_array
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_byte_array(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char in_data[] = "1234abcd";
 | 
			
		||||
    gint32 in_len = sizeof(in_data) - 1;
 | 
			
		||||
    const void* out_data = NULL;
 | 
			
		||||
    gsize out_len = 0;
 | 
			
		||||
    void* tmp;
 | 
			
		||||
    gsize tmp_len = sizeof(in_len) + in_len;
 | 
			
		||||
    gint32 null_len = -1;
 | 
			
		||||
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
 | 
			
		||||
    /* test for failed read (wrong len part of byte array) */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    tmp = g_malloc0(1);
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, 1);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, 1);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte_array(&reader, &out_len));
 | 
			
		||||
    g_assert(!gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(out_len == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
 | 
			
		||||
    /* test for failed read (wrong data part of byte array) */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    tmp = g_malloc0(in_len - 1);
 | 
			
		||||
    memcpy(tmp, &in_len, sizeof(in_len));
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, in_len - 1);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, in_len - 1);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_reader_read_byte_array(&reader, &out_len));
 | 
			
		||||
    g_assert(!gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(out_len == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
 | 
			
		||||
    /* test for empty (len 0) byte array */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    tmp = g_malloc0(sizeof(null_len));
 | 
			
		||||
    memcpy(tmp, &null_len, sizeof(null_len));
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, sizeof(null_len));
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, sizeof(null_len));
 | 
			
		||||
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert(out_len == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
 | 
			
		||||
    /* test for data */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    tmp = g_malloc0(tmp_len);
 | 
			
		||||
    memcpy(tmp, &in_len, sizeof(in_len));
 | 
			
		||||
    memcpy(tmp + sizeof(in_len), in_data, in_len);
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, tmp_len);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, tmp_len);
 | 
			
		||||
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert((gsize)in_len == out_len);
 | 
			
		||||
    g_assert(memcmp(in_data, out_data, in_len) == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * copy
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_copy(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char in_data1[] = "12345678";
 | 
			
		||||
    const char in_data2[] = "abcdefgh";
 | 
			
		||||
    gint32 in_len1 = sizeof(in_data1) - 1;
 | 
			
		||||
    gint32 in_len2 = sizeof(in_data2) - 1;
 | 
			
		||||
    const void* out_data = NULL;
 | 
			
		||||
    gsize out_len = 0;
 | 
			
		||||
    void* tmp;
 | 
			
		||||
    guint8* ptr;
 | 
			
		||||
    gsize tmp_len = 2 * sizeof(guint32) + in_len1 + in_len2;
 | 
			
		||||
 | 
			
		||||
    GBinderDriver* driver;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderReader reader2;
 | 
			
		||||
    GBinderReaderData data;
 | 
			
		||||
 | 
			
		||||
    /* test for data */
 | 
			
		||||
    g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL)));
 | 
			
		||||
    ptr = tmp = g_malloc0(tmp_len);
 | 
			
		||||
    memcpy(ptr, &in_len1, sizeof(in_len1));
 | 
			
		||||
    ptr += sizeof(in_len1);
 | 
			
		||||
    memcpy(ptr, in_data1, in_len1);
 | 
			
		||||
    ptr += in_len1;
 | 
			
		||||
    memcpy(ptr, &in_len2, sizeof(in_len2));
 | 
			
		||||
    ptr += sizeof(in_len2);
 | 
			
		||||
    memcpy(ptr, in_data2, in_len2);
 | 
			
		||||
 | 
			
		||||
    memset(&data, 0, sizeof(data));
 | 
			
		||||
    data.buffer = gbinder_buffer_new(driver, tmp, tmp_len);
 | 
			
		||||
    gbinder_reader_init(&reader, &data, 0, tmp_len);
 | 
			
		||||
 | 
			
		||||
    /* Read the first array */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert((gsize)in_len1 == out_len);
 | 
			
		||||
    g_assert(memcmp(in_data1, out_data, in_len1) == 0);
 | 
			
		||||
 | 
			
		||||
    /* Copy the reader */
 | 
			
		||||
    gbinder_reader_copy(&reader2, &reader);
 | 
			
		||||
 | 
			
		||||
    /* Read both and compare the output */
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
    g_assert((gsize)in_len2 == out_len);
 | 
			
		||||
    g_assert(memcmp(in_data2, out_data, in_len2) == 0);
 | 
			
		||||
 | 
			
		||||
    g_assert((out_data = gbinder_reader_read_byte_array(&reader2, &out_len)));
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader2));
 | 
			
		||||
    g_assert((gsize)in_len2 == out_len);
 | 
			
		||||
    g_assert(memcmp(in_data2, out_data, in_len2) == 0);
 | 
			
		||||
 | 
			
		||||
    gbinder_buffer_free(data.buffer);
 | 
			
		||||
    gbinder_driver_unref(driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/reader/"
 | 
			
		||||
#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 "empty", test_empty);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "byte", test_byte);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bool", test_bool);
 | 
			
		||||
    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_("empty"), test_empty);
 | 
			
		||||
    g_test_add_func(TEST_("byte"), test_byte);
 | 
			
		||||
    g_test_add_func(TEST_("bool"), test_bool);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_string8_tests); i++) {
 | 
			
		||||
        const TestStringData* test = test_string8_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "/string8/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("string8/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_string8);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "/string16/null", test_string16_null);
 | 
			
		||||
    g_test_add_func(TEST_("string16/null"), test_string16_null);
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) {
 | 
			
		||||
        const TestStringData* test = test_string16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "/string16/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("string16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_string16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_struct_tests); i++) {
 | 
			
		||||
        const TestHidlStruct* test = test_hidl_struct_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_struct/"), test->name,
 | 
			
		||||
            NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_struct);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) {
 | 
			
		||||
        const TestHidlVec* test = test_hidl_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "/hidl_vec/", test->name,
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_vec/"), test->name,
 | 
			
		||||
            NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_vec);
 | 
			
		||||
@@ -992,17 +1287,19 @@ int main(int argc, char* argv[])
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_err_tests); i++) {
 | 
			
		||||
        const TestHidlStringErr* test = test_hidl_string_err_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "/hidl_string/err-", test->name,
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string/err-"), test->name,
 | 
			
		||||
            NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string_err);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "/object/object", test_object);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "/object/object/invalid", test_object_invalid);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "/object/object/no_reg", test_object_no_reg);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "/vec", test_vec);
 | 
			
		||||
    g_test_add_func(TEST_("object/valid"), test_object);
 | 
			
		||||
    g_test_add_func(TEST_("object/invalid"), test_object_invalid);
 | 
			
		||||
    g_test_add_func(TEST_("object/no_reg"), test_object_no_reg);
 | 
			
		||||
    g_test_add_func(TEST_("vec"), test_vec);
 | 
			
		||||
    g_test_add_func(TEST_("byte_array"), test_byte_array);
 | 
			
		||||
    g_test_add_func(TEST_("copy"), test_copy);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										684
									
								
								unit/unit_servicemanager/unit_servicemanager.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										684
									
								
								unit/unit_servicemanager/unit_servicemanager.c
									
									
									
									
									
										Normal 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:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicepoll/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicepoll/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicepoll
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										448
									
								
								unit/unit_servicepoll/unit_servicepoll.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										448
									
								
								unit/unit_servicepoll/unit_servicepoll.c
									
									
									
									
									
										Normal 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:
 | 
			
		||||
 */
 | 
			
		||||
@@ -74,6 +74,7 @@ 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_bytes(NULL, NULL, 0);
 | 
			
		||||
@@ -91,6 +92,8 @@ 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);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(NULL));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(NULL));
 | 
			
		||||
@@ -202,9 +205,9 @@ test_bool(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    const char encoded[] = {
 | 
			
		||||
        0x00, 0xff, 0xff, 0xff,
 | 
			
		||||
        0x01, 0xff, 0xff, 0xff,
 | 
			
		||||
        0x01, 0xff, 0xff, 0xff
 | 
			
		||||
        0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00,
 | 
			
		||||
        0x01, 0x00, 0x00, 0x00
 | 
			
		||||
    };
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
@@ -340,6 +343,75 @@ test_string16(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * utf16
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_utf16_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const gunichar2* in;
 | 
			
		||||
    gssize in_len;
 | 
			
		||||
    const guint8* out;
 | 
			
		||||
    gssize out_len;
 | 
			
		||||
} TestUtf16Data;
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_null[] = {
 | 
			
		||||
    TEST_INT32_BYTES(-1)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_empty[] = {
 | 
			
		||||
    TEST_INT32_BYTES(0),
 | 
			
		||||
    0x00, 0x00, 0xff, 0xff
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_x[] = {
 | 
			
		||||
    TEST_INT32_BYTES(1),
 | 
			
		||||
    TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_xy[] = {
 | 
			
		||||
    TEST_INT32_BYTES(2),
 | 
			
		||||
    TEST_INT16_BYTES('x'), TEST_INT16_BYTES('y'),
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const gunichar2 utf16_tests_input_empty[] = { 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_x[] = { 'x', 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_xy[] = { 'x', 'y', 0 };
 | 
			
		||||
 | 
			
		||||
static const TestUtf16Data test_utf16_tests[] = {
 | 
			
		||||
    { "null", NULL, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_null) },
 | 
			
		||||
    { "empty", utf16_tests_input_empty, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_empty) },
 | 
			
		||||
    { "1", utf16_tests_input_x, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "2", utf16_tests_input_xy, 1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "3", utf16_tests_input_xy, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_xy) }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_utf16(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestUtf16Data* test = test_data;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_string16_utf16(&writer, test->in, test->in_len);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == test->out_len);
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, test->out, test->out_len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_vec
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -364,15 +436,15 @@ static guint test_hidl_vec_offsets_64[] =
 | 
			
		||||
 | 
			
		||||
static const TestHidlVecData test_hidl_vec_tests[] = {
 | 
			
		||||
    { "32/null", &gbinder_io_32, NULL, 0, 0,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(HidlVec) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "32/2x1", &gbinder_io_32, "xy", 2, 1,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_32),
 | 
			
		||||
      sizeof(HidlVec) + 8 /* vec data aligned at 8 bytes boundary */ },
 | 
			
		||||
      sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ },
 | 
			
		||||
    { "64/null", &gbinder_io_64, NULL, 0, 0,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(HidlVec) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(GBinderHidlVec) },
 | 
			
		||||
    { "64/2x2", &gbinder_io_64, "xxyy", 2, 2,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_64),
 | 
			
		||||
      sizeof(HidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }
 | 
			
		||||
      sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -423,15 +495,17 @@ static guint test_hidl_string_offsets_64[] =
 | 
			
		||||
 | 
			
		||||
static const TestHidlStringData test_hidl_string_tests[] = {
 | 
			
		||||
    { "32/null", &gbinder_io_32, NULL,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0), sizeof(HidlString) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0),
 | 
			
		||||
      sizeof(GBinderHidlString) },
 | 
			
		||||
    { "32/xxx", &gbinder_io_32, "xxx",
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
 | 
			
		||||
      sizeof(HidlString) + 8 /* string data aligned at 8 bytes boundary */ },
 | 
			
		||||
      sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ },
 | 
			
		||||
    { "64/null", &gbinder_io_64, NULL,
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0), sizeof(HidlString) },
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0),
 | 
			
		||||
      sizeof(GBinderHidlString) },
 | 
			
		||||
    { "64/xxxxxxx", &gbinder_io_64, "xxxxxxx",
 | 
			
		||||
      TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
 | 
			
		||||
      sizeof(HidlString) + 8 /* string data aligned at 8 bytes boundary */ }
 | 
			
		||||
      sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -479,8 +553,9 @@ test_hidl_string2(
 | 
			
		||||
    g_assert(offsets->data[0] == 0);
 | 
			
		||||
    g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    g_assert(offsets->data[2] == 2*BUFFER_OBJECT_SIZE_32);
 | 
			
		||||
    /* 2 HidlStrings + "foo" aligned at 8 bytes boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) == 2*sizeof(HidlString)+8);
 | 
			
		||||
    /* 2 GBinderHidlStrings + "foo" aligned at 8 bytes boundary */
 | 
			
		||||
    g_assert(gbinder_output_data_buffers_size(data) ==
 | 
			
		||||
        (2 * sizeof(GBinderHidlString) + 8));
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
@@ -511,18 +586,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
 | 
			
		||||
@@ -683,46 +758,107 @@ 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_("int32"), test_int32);
 | 
			
		||||
    g_test_add_func(TEST_("int64"), test_int64);
 | 
			
		||||
    g_test_add_func(TEST_("float"), test_float);
 | 
			
		||||
    g_test_add_func(TEST_("double"), test_double);
 | 
			
		||||
    g_test_add_func(TEST_("bool"), test_bool);
 | 
			
		||||
    g_test_add_func(TEST_("bytes"), test_bytes);
 | 
			
		||||
    g_test_add_func(TEST_("string8"), test_string8);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) {
 | 
			
		||||
        const TestString16Data* test = test_string16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "string16/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("string16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_string16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_utf16_tests); i++) {
 | 
			
		||||
        const TestUtf16Data* test = test_utf16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_("utf16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_utf16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) {
 | 
			
		||||
        const TestHidlVecData* test = test_hidl_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_vec/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_vec/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_vec);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "hidl_string/2strings", test_hidl_string2);
 | 
			
		||||
    g_test_add_func(TEST_("hidl_string/2strings"), test_hidl_string2);
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_tests); i++) {
 | 
			
		||||
        const TestHidlStringData* test = test_hidl_string_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_string/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
@@ -730,17 +866,17 @@ 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_("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();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user