Compare commits
	
		
			24 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					66f08bf0db | ||
| 
						 | 
					90384e921f | ||
| 
						 | 
					1ae7c2697c | ||
| 
						 | 
					536143c1d7 | ||
| 
						 | 
					6025950aab | ||
| 
						 | 
					4d69940b96 | ||
| 
						 | 
					b1f49cae13 | ||
| 
						 | 
					80e9be5343 | ||
| 
						 | 
					d6d6b76fa5 | ||
| 
						 | 
					d18a352a82 | ||
| 
						 | 
					5fbaabb47e | ||
| 
						 | 
					83cc13b817 | ||
| 
						 | 
					1679cda4c5 | ||
| 
						 | 
					e5f11aafc1 | ||
| 
						 | 
					f262c77b17 | ||
| 
						 | 
					3ef00effc5 | ||
| 
						 | 
					1ce13bea91 | ||
| 
						 | 
					eaab366dcb | ||
| 
						 | 
					784f06c415 | ||
| 
						 | 
					4b07e80a8f | ||
| 
						 | 
					9f7fac407d | ||
| 
						 | 
					47b6668876 | ||
| 
						 | 
					951977961b | ||
| 
						 | 
					f069c3a595 | 
							
								
								
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 | 
			
		||||
You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
VERSION_MAJOR = 1
 | 
			
		||||
VERSION_MINOR = 1
 | 
			
		||||
VERSION_RELEASE = 0
 | 
			
		||||
VERSION_RELEASE = 4
 | 
			
		||||
 | 
			
		||||
# Version for pkg-config
 | 
			
		||||
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,34 @@
 | 
			
		||||
libgbinder (1.1.4) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed a threading issue
 | 
			
		||||
  * Decode NULL object reference
 | 
			
		||||
  * Added new basic HIDL types
 | 
			
		||||
  * Set TF_ACCEPT_FDS transaction flag
 | 
			
		||||
  * Added servicemanager_hidl unit test
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Thu, 21 Jan 2021 03:34:45 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.3) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Improved unit test coverage
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 23 Dec 2020 21:48:27 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.2) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Fixed random unit text failures
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Wed, 23 Dec 2020 12:39:22 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.1) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Handle corner cases for abandoned loopers
 | 
			
		||||
  * Pass 0x0f priority to aidl2 service list request.
 | 
			
		||||
  * Improved binder simulation for unit tests
 | 
			
		||||
  * Added servicemanager_aidl unit test
 | 
			
		||||
 | 
			
		||||
 -- Slava Monich <slava.monich@jolla.com>  Tue, 22 Dec 2020 15:15:10 +0200
 | 
			
		||||
 | 
			
		||||
libgbinder (1.1.0) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Made RPC protocol configurable per binder device
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -14,8 +14,8 @@
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *      this software without specific prior written permission.
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
@@ -93,14 +93,16 @@ gbinder_servicemanager_new_local_object(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_servicemanager_new_local_object2(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc handler,
 | 
			
		||||
    void* user_data); /* Since 1.0.29 */
 | 
			
		||||
    void* user_data) /* Since 1.0.29 */
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT;
 | 
			
		||||
 | 
			
		||||
GBinderServiceManager*
 | 
			
		||||
gbinder_servicemanager_ref(
 | 
			
		||||
@@ -127,7 +129,9 @@ gbinder_servicemanager_list(
 | 
			
		||||
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_list_sync(
 | 
			
		||||
    GBinderServiceManager* sm);
 | 
			
		||||
    GBinderServiceManager* sm)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT
 | 
			
		||||
    G_GNUC_MALLOC;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_servicemanager_get_service(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -76,16 +76,20 @@ typedef struct gbinder_parent GBinderParent;
 | 
			
		||||
 | 
			
		||||
/* Basic HIDL types */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_ALIGNED(x) __attribute__ ((aligned(x)))
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_vec {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const void* ptr;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 count;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[3];
 | 
			
		||||
} GBinderHidlVec;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_VEC_BUFFER_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlVec) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_string {
 | 
			
		||||
    union {
 | 
			
		||||
@@ -93,10 +97,51 @@ typedef struct gbinder_hidl_string {
 | 
			
		||||
        const char* str;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint32 len;
 | 
			
		||||
    guint32 owns_buffer;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[3];
 | 
			
		||||
} GBinderHidlString;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_STRING_BUFFER_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlString) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_fds {
 | 
			
		||||
    guint32 version GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 num_fds GBINDER_ALIGNED(4);
 | 
			
		||||
    guint32 num_ints GBINDER_ALIGNED(4);
 | 
			
		||||
} GBINDER_ALIGNED(4) GBinderFds;  /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
/* Actual fds immediately follow GBinderFds: */
 | 
			
		||||
#define gbinder_fds_get_fd(fds,i) (((const int*)((fds) + 1))[i])
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_FDS_VERSION (12)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderFds) == GBINDER_HIDL_FDS_VERSION);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_handle {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const GBinderFds* fds;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint8 owns_handle;
 | 
			
		||||
    guint8 pad[7];
 | 
			
		||||
} GBinderHidlHandle; /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_HANDLE_VALUE_OFFSET (0)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlHandle) == 16);
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_hidl_memory {
 | 
			
		||||
    union {
 | 
			
		||||
        guint64 value;
 | 
			
		||||
        const GBinderFds* fds;
 | 
			
		||||
    } data;
 | 
			
		||||
    guint8 owns_buffer;
 | 
			
		||||
    guint8 pad[7];
 | 
			
		||||
    guint64 size;
 | 
			
		||||
    GBinderHidlString name;
 | 
			
		||||
} GBinderHidlMemory; /* Since 1.1.4 */
 | 
			
		||||
 | 
			
		||||
#define GBINDER_HIDL_MEMORY_PTR_OFFSET (0)
 | 
			
		||||
#define GBINDER_HIDL_MEMORY_NAME_OFFSET (24)
 | 
			
		||||
G_STATIC_ASSERT(sizeof(GBinderHidlMemory) == 40);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Each RPC call is identified by the interface name returned
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
Name: libgbinder
 | 
			
		||||
Version: 1.1.0
 | 
			
		||||
Version: 1.1.4
 | 
			
		||||
Release: 0
 | 
			
		||||
Summary: Binder client library
 | 
			
		||||
License: BSD
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -178,6 +178,66 @@ gbinder_client_transact_destroy(
 | 
			
		||||
    g_slice_free(GBinderClientTx, tx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internal interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_client_transact_sync_reply2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return api->sync_reply(obj->ipc, obj->handle, code, req, status);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_client_transact_sync_oneway2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return api->sync_oneway(obj->ipc, obj->handle, code, req);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
        return (-ESTALE);
 | 
			
		||||
    }
 | 
			
		||||
    return (-EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -319,25 +379,8 @@ gbinder_client_transact_sync_reply(
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_reply(obj->ipc, obj->handle,
 | 
			
		||||
                code, req, status);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
    return gbinder_client_transact_sync_reply2(self, code, req, status,
 | 
			
		||||
        &gbinder_ipc_sync_main);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -346,26 +389,8 @@ gbinder_client_transact_sync_oneway(
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderRemoteObject* obj = self->remote;
 | 
			
		||||
 | 
			
		||||
        if (G_LIKELY(!obj->dead)) {
 | 
			
		||||
            if (!req) {
 | 
			
		||||
                const GBinderClientIfaceRange* r = gbinder_client_find_range
 | 
			
		||||
                    (gbinder_client_cast(self), code);
 | 
			
		||||
 | 
			
		||||
                /* Default empty request (just the header, no parameters) */
 | 
			
		||||
                if (r) {
 | 
			
		||||
                    req = r->basic_req;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return gbinder_ipc_transact_sync_oneway(obj->ipc, obj->handle,
 | 
			
		||||
                code, req);
 | 
			
		||||
        }
 | 
			
		||||
        GDEBUG("Refusing to perform transaction with a dead object");
 | 
			
		||||
        return (-ESTALE);
 | 
			
		||||
    }
 | 
			
		||||
    return (-EINVAL);
 | 
			
		||||
    return gbinder_client_transact_sync_oneway2(self, code, req,
 | 
			
		||||
        &gbinder_ipc_sync_main);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -41,6 +41,24 @@ struct gbinder_client {
 | 
			
		||||
    GBinderRemoteObject* remote;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_client_transact_sync_reply2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
    G_GNUC_WARN_UNUSED_RESULT
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_client_transact_sync_oneway2(
 | 
			
		||||
    GBinderClient* self,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
#define gbinder_client_ipc(client) ((client)->remote->ipc)
 | 
			
		||||
 | 
			
		||||
#endif /* GBINDER_CLIENT_PRIVATE_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -713,7 +713,7 @@ gbinder_driver_new(
 | 
			
		||||
                            max_threads, strerror(errno));
 | 
			
		||||
                    }
 | 
			
		||||
                    /* Choose the protocol based on the device name
 | 
			
		||||
                     * if none is explicitely specified */
 | 
			
		||||
                     * if none is explicitly specified */
 | 
			
		||||
                    self->protocol = protocol ? protocol :
 | 
			
		||||
                        gbinder_rpc_protocol_for_device(dev);
 | 
			
		||||
                    return self;
 | 
			
		||||
@@ -953,7 +953,7 @@ gbinder_driver_read(
 | 
			
		||||
    if (ret >= 0) {
 | 
			
		||||
        /* Loop until we have handled all the incoming commands */
 | 
			
		||||
        gbinder_driver_handle_commands(self, reg, handler, &rb);
 | 
			
		||||
        while (rb.buf.consumed) {
 | 
			
		||||
        while (rb.buf.consumed && gbinder_handler_can_loop(handler)) {
 | 
			
		||||
            ret = gbinder_driver_write_read(self, NULL, &rb.buf);
 | 
			
		||||
            if (ret >= 0) {
 | 
			
		||||
                gbinder_driver_handle_commands(self, reg, handler, &rb);
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
#include "gbinder_types_p.h"
 | 
			
		||||
 | 
			
		||||
typedef struct gbinder_handler_functions {
 | 
			
		||||
    gboolean (*can_loop)(GBinderHandler* handler);
 | 
			
		||||
    GBinderLocalReply* (*transact)(GBinderHandler* handler,
 | 
			
		||||
        GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code,
 | 
			
		||||
        guint flags, int* status);
 | 
			
		||||
@@ -47,6 +48,14 @@ struct gbinder_handler {
 | 
			
		||||
 | 
			
		||||
/* Inline wrappers */
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_handler_can_loop(
 | 
			
		||||
    GBinderHandler* self)
 | 
			
		||||
{
 | 
			
		||||
    return self && self->f->can_loop && self->f->can_loop(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBINDER_INLINE_FUNC
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_handler_transact(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -118,7 +118,7 @@ GBINDER_IO_FN(encode_pointer)(
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Encodes flat_buffer_object */
 | 
			
		||||
/* Encodes flat_binder_object */
 | 
			
		||||
static
 | 
			
		||||
guint
 | 
			
		||||
GBINDER_IO_FN(encode_local_object)(
 | 
			
		||||
@@ -163,12 +163,12 @@ GBINDER_IO_FN(encode_fd_object)(
 | 
			
		||||
    void* out,
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    struct flat_binder_object* dest = out;
 | 
			
		||||
    struct binder_fd_object* dest = out;
 | 
			
		||||
 | 
			
		||||
    memset(dest, 0, sizeof(*dest));
 | 
			
		||||
    dest->hdr.type = BINDER_TYPE_FD;
 | 
			
		||||
    dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
    dest->handle = fd;
 | 
			
		||||
    dest->pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 | 
			
		||||
    dest->fd = fd;
 | 
			
		||||
    return sizeof(*dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -226,9 +226,7 @@ GBINDER_IO_FN(fill_transaction_data)(
 | 
			
		||||
    tr->code = code;
 | 
			
		||||
    tr->data_size = payload->len;
 | 
			
		||||
    tr->data.ptr.buffer = (uintptr_t)payload->data;
 | 
			
		||||
    if (flags & GBINDER_TX_FLAG_ONEWAY) {
 | 
			
		||||
        tr->flags |= TF_ONE_WAY;
 | 
			
		||||
    }
 | 
			
		||||
    tr->flags |= (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS;
 | 
			
		||||
    if (offsets && offsets->count) {
 | 
			
		||||
        guint i;
 | 
			
		||||
        binder_size_t* tx_offsets = g_new(binder_size_t, offsets->count);
 | 
			
		||||
@@ -399,6 +397,15 @@ GBINDER_IO_FN(decode_binder_object)(
 | 
			
		||||
                *out = gbinder_object_registry_get_remote(reg, obj->handle);
 | 
			
		||||
            }
 | 
			
		||||
            return sizeof(*obj);
 | 
			
		||||
        case BINDER_TYPE_BINDER:
 | 
			
		||||
            if (!obj->binder) {
 | 
			
		||||
                /* That's a NULL reference */
 | 
			
		||||
                if (out) {
 | 
			
		||||
                    *out = NULL;
 | 
			
		||||
                }
 | 
			
		||||
                return sizeof(*obj);
 | 
			
		||||
            }
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            GERR("Unsupported binder object type 0x%08x", obj->hdr.type);
 | 
			
		||||
            break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -210,6 +210,23 @@ GBinderIpcLooper*
 | 
			
		||||
gbinder_ipc_looper_new(
 | 
			
		||||
    GBinderIpc* ipc);
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req);
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Utilities
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -606,6 +623,16 @@ gbinder_ipc_looper_count_primary(
 | 
			
		||||
    return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_looper_can_loop(
 | 
			
		||||
    GBinderHandler* handler)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler);
 | 
			
		||||
 | 
			
		||||
    return !g_atomic_int_get(&looper->exit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
gbinder_ipc_looper_transact(
 | 
			
		||||
@@ -824,6 +851,7 @@ gbinder_ipc_looper_new(
 | 
			
		||||
    /* Note: this call can actually fail */
 | 
			
		||||
    if (!pipe(fd)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_functions = {
 | 
			
		||||
            .can_loop = gbinder_ipc_looper_can_loop,
 | 
			
		||||
            .transact = gbinder_ipc_looper_transact
 | 
			
		||||
        };
 | 
			
		||||
        GError* error = NULL;
 | 
			
		||||
@@ -896,13 +924,15 @@ gbinder_ipc_looper_stop(
 | 
			
		||||
    GBinderIpcLooper* looper)
 | 
			
		||||
{
 | 
			
		||||
    /* Caller checks looper for NULL */
 | 
			
		||||
    if (looper->thread && looper->thread != g_thread_self()) {
 | 
			
		||||
        guint8 done = TX_DONE;
 | 
			
		||||
 | 
			
		||||
    if (looper->thread) {
 | 
			
		||||
        GDEBUG("Stopping looper %s", looper->name);
 | 
			
		||||
        g_atomic_int_set(&looper->exit, TRUE);
 | 
			
		||||
        if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
 | 
			
		||||
            looper->thread = NULL;
 | 
			
		||||
        if (looper->thread != g_thread_self()) {
 | 
			
		||||
            guint8 done = TX_DONE;
 | 
			
		||||
 | 
			
		||||
            if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
 | 
			
		||||
                looper->thread = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1420,29 +1450,15 @@ void
 | 
			
		||||
gbinder_ipc_tx_internal_exec(
 | 
			
		||||
    GBinderIpcTxPriv* priv)
 | 
			
		||||
{
 | 
			
		||||
    static const GBinderHandlerFunctions handler_fn = {
 | 
			
		||||
        gbinder_ipc_tx_handler_transact
 | 
			
		||||
    };
 | 
			
		||||
    GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv);
 | 
			
		||||
    GBinderIpcTx* pub = &priv->pub;
 | 
			
		||||
    GBinderIpc* self = pub->ipc;
 | 
			
		||||
    GBinderObjectRegistry* reg = &self->priv->object_registry;
 | 
			
		||||
    GBinderHandler handler = { &handler_fn };
 | 
			
		||||
    GBinderIpc* ipc = priv->pub.ipc;
 | 
			
		||||
 | 
			
		||||
    /* Perform synchronous transaction */
 | 
			
		||||
    if (tx->flags & GBINDER_TX_FLAG_ONEWAY) {
 | 
			
		||||
        tx->status = gbinder_driver_transact(self->driver, reg, &handler,
 | 
			
		||||
            tx->handle, tx->code, tx->req, NULL);
 | 
			
		||||
        tx->status = gbinder_ipc_transact_sync_oneway_worker(ipc, tx->handle,
 | 
			
		||||
            tx->code, tx->req);
 | 
			
		||||
    } else {
 | 
			
		||||
        tx->reply = gbinder_remote_reply_new(&self->priv->object_registry);
 | 
			
		||||
        tx->status = gbinder_driver_transact(self->driver, reg, &handler,
 | 
			
		||||
            tx->handle, tx->code, tx->req, tx->reply);
 | 
			
		||||
        if (tx->status != GBINDER_STATUS_OK &&
 | 
			
		||||
            gbinder_remote_reply_is_empty(tx->reply)) {
 | 
			
		||||
            /* Drop useless reply */
 | 
			
		||||
            gbinder_remote_reply_unref(tx->reply);
 | 
			
		||||
            tx->reply = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        tx->reply = gbinder_ipc_transact_sync_reply_worker(ipc, tx->handle,
 | 
			
		||||
            tx->code, tx->req, &tx->status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1565,6 +1581,128 @@ gbinder_ipc_tx_proc(
 | 
			
		||||
    gbinder_idle_callback_schedule(tx->completion);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcSyncApi for worker threads
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    /* Must be invoked on worker thread */
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_fn = {
 | 
			
		||||
            .can_loop = NULL,
 | 
			
		||||
            .transact = gbinder_ipc_tx_handler_transact
 | 
			
		||||
        };
 | 
			
		||||
        GBinderHandler handler = { &handler_fn };
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, &handler,
 | 
			
		||||
            handle, code, req, reply);
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway_worker(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    /* Must be invoked on worker thread */
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        static const GBinderHandlerFunctions handler_fn = {
 | 
			
		||||
            .can_loop = NULL,
 | 
			
		||||
            .transact = gbinder_ipc_tx_handler_transact
 | 
			
		||||
        };
 | 
			
		||||
        GBinderHandler handler = { &handler_fn };
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            &handler, handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GBinderIpcSyncApi gbinder_ipc_sync_worker = {
 | 
			
		||||
    .sync_reply = gbinder_ipc_transact_sync_reply_worker,
 | 
			
		||||
    .sync_oneway = gbinder_ipc_transact_sync_oneway_worker
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * GBinderIpcSyncApi for the main thread
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
 | 
			
		||||
            code, req, reply);
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            NULL, handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GBinderIpcSyncApi gbinder_ipc_sync_main = {
 | 
			
		||||
    .sync_reply = gbinder_ipc_transact_sync_reply,
 | 
			
		||||
    .sync_oneway = gbinder_ipc_transact_sync_oneway
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -1638,50 +1776,6 @@ gbinder_ipc_object_registry(
 | 
			
		||||
    return G_LIKELY(self) ? &self->priv->object_registry : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
        GBinderObjectRegistry* reg = &priv->object_registry;
 | 
			
		||||
        GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
 | 
			
		||||
        int ret = gbinder_driver_transact(self->driver, reg, NULL, handle,
 | 
			
		||||
            code, req, reply);
 | 
			
		||||
 | 
			
		||||
        if (status) *status = ret;
 | 
			
		||||
        if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) {
 | 
			
		||||
            return reply;
 | 
			
		||||
        } else {
 | 
			
		||||
            gbinder_remote_reply_unref(reply);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (status) *status = (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        GBinderIpcPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
        return gbinder_driver_transact(self->driver, &priv->object_registry,
 | 
			
		||||
            NULL, handle, code, req, NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_ipc_transact(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
@@ -1749,6 +1843,14 @@ gbinder_ipc_cancel(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_set_max_threads(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    gint max)
 | 
			
		||||
{
 | 
			
		||||
    return g_thread_pool_set_max_threads(self->priv->tx_pool, max, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -67,6 +67,31 @@ void
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
(*GBinderIpcSyncReplyFunc)(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status);
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
int
 | 
			
		||||
(*GBinderIpcSyncOnewayFunc)(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req);
 | 
			
		||||
 | 
			
		||||
struct gbinder_ipc_sync_api {
 | 
			
		||||
    GBinderIpcSyncReplyFunc sync_reply;
 | 
			
		||||
    GBinderIpcSyncOnewayFunc sync_oneway;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const GBinderIpcSyncApi gbinder_ipc_sync_main GBINDER_INTERNAL;
 | 
			
		||||
extern const GBinderIpcSyncApi gbinder_ipc_sync_worker GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
gbinder_ipc_new(
 | 
			
		||||
    const char* dev)
 | 
			
		||||
@@ -111,23 +136,6 @@ gbinder_ipc_invalidate_remote_handle(
 | 
			
		||||
    guint32 handle)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
GBinderRemoteReply*
 | 
			
		||||
gbinder_ipc_transact_sync_reply(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req,
 | 
			
		||||
    int* status)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_ipc_transact_sync_oneway(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    guint32 handle,
 | 
			
		||||
    guint32 code,
 | 
			
		||||
    GBinderLocalRequest* req)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_ipc_transact(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
@@ -169,6 +177,13 @@ gbinder_ipc_remote_object_disposed(
 | 
			
		||||
    GBinderRemoteObject* obj)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Needed by unit tests */
 | 
			
		||||
gboolean
 | 
			
		||||
gbinder_ipc_set_max_threads(
 | 
			
		||||
    GBinderIpc* self,
 | 
			
		||||
    gint max_threads)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
/* Declared for unit tests */
 | 
			
		||||
void
 | 
			
		||||
gbinder_ipc_exit(
 | 
			
		||||
 
 | 
			
		||||
@@ -339,38 +339,50 @@ gbinder_local_object_new(
 | 
			
		||||
    if (G_LIKELY(ipc)) {
 | 
			
		||||
        GBinderLocalObject* self = g_object_new
 | 
			
		||||
            (GBINDER_TYPE_LOCAL_OBJECT, NULL);
 | 
			
		||||
        GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
        guint i = 0, n = gutil_strv_length((char**)ifaces);
 | 
			
		||||
        gboolean append_base_interface;
 | 
			
		||||
 | 
			
		||||
        if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
 | 
			
		||||
            append_base_interface = TRUE;
 | 
			
		||||
            n++;
 | 
			
		||||
        } else {
 | 
			
		||||
            append_base_interface = FALSE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        priv->ifaces = g_new(char*, n + 1);
 | 
			
		||||
        if (ifaces) {
 | 
			
		||||
            while (*ifaces) {
 | 
			
		||||
                priv->ifaces[i++] = g_strdup(*ifaces++);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (append_base_interface) {
 | 
			
		||||
            priv->ifaces[i++] = g_strdup(hidl_base_interface);
 | 
			
		||||
        }
 | 
			
		||||
        priv->ifaces[i] = NULL;
 | 
			
		||||
 | 
			
		||||
        self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
        self->ifaces = (const char**)priv->ifaces;
 | 
			
		||||
        priv->txproc = txproc;
 | 
			
		||||
        priv->user_data = user_data;
 | 
			
		||||
        gbinder_local_object_init_base(self, ipc, ifaces, txproc, user_data);
 | 
			
		||||
        gbinder_ipc_register_local_object(ipc, self);
 | 
			
		||||
        return self;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_init_base(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalObjectPriv* priv = self->priv;
 | 
			
		||||
    guint i = 0, n = gutil_strv_length((char**)ifaces);
 | 
			
		||||
    gboolean append_base_interface;
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
 | 
			
		||||
        append_base_interface = TRUE;
 | 
			
		||||
        n++;
 | 
			
		||||
    } else {
 | 
			
		||||
        append_base_interface = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    priv->ifaces = g_new(char*, n + 1);
 | 
			
		||||
    if (ifaces) {
 | 
			
		||||
        while (*ifaces) {
 | 
			
		||||
            priv->ifaces[i++] = g_strdup(*ifaces++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (append_base_interface) {
 | 
			
		||||
        priv->ifaces[i++] = g_strdup(hidl_base_interface);
 | 
			
		||||
    }
 | 
			
		||||
    priv->ifaces[i] = NULL;
 | 
			
		||||
 | 
			
		||||
    self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
    self->ifaces = (const char**)priv->ifaces;
 | 
			
		||||
    priv->txproc = txproc;
 | 
			
		||||
    priv->user_data = user_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderLocalObject*
 | 
			
		||||
gbinder_local_object_ref(
 | 
			
		||||
    GBinderLocalObject* self)
 | 
			
		||||
 
 | 
			
		||||
@@ -83,10 +83,21 @@ GType gbinder_local_object_get_type(void) GBINDER_INTERNAL;
 | 
			
		||||
#define GBINDER_TYPE_LOCAL_OBJECT (gbinder_local_object_get_type())
 | 
			
		||||
#define GBINDER_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObject))
 | 
			
		||||
#define GBINDER_LOCAL_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectClass)
 | 
			
		||||
 | 
			
		||||
#define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
 | 
			
		||||
#define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_local_object_init_base(
 | 
			
		||||
    GBinderLocalObject* self,
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    const char* const* ifaces,
 | 
			
		||||
    GBinderLocalTransactFunc txproc,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
    GBINDER_INTERNAL;
 | 
			
		||||
 | 
			
		||||
gulong
 | 
			
		||||
gbinder_local_object_add_weak_refs_changed_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,7 @@ gbinder_remote_object_reanimate(
 | 
			
		||||
        if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
 | 
			
		||||
            /* Wow, it's alive! */
 | 
			
		||||
            self->dead = FALSE;
 | 
			
		||||
            gbinder_ipc_looper_check(self->ipc); /* For death notifications */
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, self->handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        }
 | 
			
		||||
@@ -151,6 +152,7 @@ gbinder_remote_object_new(
 | 
			
		||||
        self->ipc = gbinder_ipc_ref(ipc);
 | 
			
		||||
        self->handle = handle;
 | 
			
		||||
        if (!(self->dead = dead)) {
 | 
			
		||||
            gbinder_ipc_looper_check(self->ipc); /* For death notifications */
 | 
			
		||||
            gbinder_driver_acquire(ipc->driver, handle);
 | 
			
		||||
            gbinder_driver_request_death_notification(ipc->driver, self);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -199,7 +199,8 @@ gbinder_servicemanager_list_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerListTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->list(data->sm);
 | 
			
		||||
    data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        list(data->sm, &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -243,8 +244,9 @@ gbinder_servicemanager_get_service_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerGetServiceTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->get_service
 | 
			
		||||
            (data->sm, data->name, &data->status);
 | 
			
		||||
    data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        get_service(data->sm, data->name, &data->status,
 | 
			
		||||
            &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -286,8 +288,8 @@ gbinder_servicemanager_add_service_tx_exec(
 | 
			
		||||
{
 | 
			
		||||
    GBinderServiceManagerAddServiceTxData* data = tx->user_data;
 | 
			
		||||
 | 
			
		||||
    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->add_service
 | 
			
		||||
            (data->sm, data->name, data->obj);
 | 
			
		||||
    data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)->
 | 
			
		||||
        add_service(data->sm, data->name, data->obj, &gbinder_ipc_sync_worker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -776,7 +778,8 @@ gbinder_servicemanager_list_sync(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self)) {
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->list(self);
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            list(self, &gbinder_ipc_sync_main);
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -815,8 +818,8 @@ gbinder_servicemanager_get_service_sync(
 | 
			
		||||
    GBinderRemoteObject* obj = NULL;
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(self) && name) {
 | 
			
		||||
        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->get_service
 | 
			
		||||
            (self, name, status);
 | 
			
		||||
        obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            get_service(self, name, status, &gbinder_ipc_sync_main);
 | 
			
		||||
        if (obj) {
 | 
			
		||||
            GBinderServiceManagerPriv* priv = self->priv;
 | 
			
		||||
 | 
			
		||||
@@ -866,8 +869,8 @@ gbinder_servicemanager_add_service_sync(
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
{
 | 
			
		||||
    if (G_LIKELY(self) && name && obj) {
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->add_service
 | 
			
		||||
            (self, name, obj);
 | 
			
		||||
        return GBINDER_SERVICEMANAGER_GET_CLASS(self)->
 | 
			
		||||
            add_service(self, name, obj, &gbinder_ipc_sync_main);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (-EINVAL);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -35,9 +35,9 @@
 | 
			
		||||
#include "gbinder_servicemanager_aidl.h"
 | 
			
		||||
#include "gbinder_servicepoll.h"
 | 
			
		||||
#include "gbinder_eventloop_p.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
 | 
			
		||||
@@ -172,7 +172,8 @@ gbinder_servicemanager_aidl_add_service_req(
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_aidl_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray* list = g_ptr_array_new();
 | 
			
		||||
    GBinderClient* client = manager->client;
 | 
			
		||||
@@ -181,8 +182,8 @@ gbinder_servicemanager_aidl_list(
 | 
			
		||||
    GBinderLocalRequest* req = klass->list_services_req(client, 0);
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
 | 
			
		||||
    while ((reply = gbinder_client_transact_sync_reply(client,
 | 
			
		||||
        LIST_SERVICES_TRANSACTION, req, NULL)) != NULL) {
 | 
			
		||||
    while ((reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        LIST_SERVICES_TRANSACTION, req, NULL, api)) != NULL) {
 | 
			
		||||
        char* service = gbinder_remote_reply_read_string16(reply);
 | 
			
		||||
 | 
			
		||||
        gbinder_remote_reply_unref(reply);
 | 
			
		||||
@@ -205,15 +206,16 @@ GBinderRemoteObject*
 | 
			
		||||
gbinder_servicemanager_aidl_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* obj;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_string16(req, name);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        CHECK_SERVICE_TRANSACTION, req, status);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
        CHECK_SERVICE_TRANSACTION, req, status, api);
 | 
			
		||||
 | 
			
		||||
    obj = gbinder_remote_reply_read_object(reply);
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
@@ -226,14 +228,15 @@ int
 | 
			
		||||
gbinder_servicemanager_aidl_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
    GBinderClient* client = manager->client;
 | 
			
		||||
    GBinderLocalRequest* req = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS
 | 
			
		||||
        (manager)->add_service_req(client, name, obj);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply(client,
 | 
			
		||||
        ADD_SERVICE_TRANSACTION, req, &status);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2(client,
 | 
			
		||||
        ADD_SERVICE_TRANSACTION, req, &status, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,8 @@ G_DEFINE_TYPE(GBinderServiceManagerAidl2,
 | 
			
		||||
    GBINDER_TYPE_SERVICEMANAGER_AIDL)
 | 
			
		||||
 | 
			
		||||
#define PARENT_CLASS gbinder_servicemanager_aidl2_parent_class
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_DEFAULT (8)
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_DEFAULT (0x08)
 | 
			
		||||
#define DUMP_FLAG_PRIORITY_ALL     (0x0f)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalRequest*
 | 
			
		||||
@@ -56,7 +57,7 @@ gbinder_servicemanager_aidl2_list_services_req(
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(client);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_append_int32(req, index);
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT);
 | 
			
		||||
    gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL);
 | 
			
		||||
    return req;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -31,9 +31,9 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gbinder_client.h>
 | 
			
		||||
#include <gbinder_local_object.h>
 | 
			
		||||
#include <gbinder_local_request.h>
 | 
			
		||||
#include <gbinder_remote_reply.h>
 | 
			
		||||
@@ -147,11 +147,12 @@ gbinder_servicemanager_hidl_notification(
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
gbinder_servicemanager_hidl_list(
 | 
			
		||||
    GBinderServiceManager* self)
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_client_new_request(self->client);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply
 | 
			
		||||
        (self->client, LIST_TRANSACTION, req, NULL);
 | 
			
		||||
    GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2
 | 
			
		||||
        (self->client, LIST_TRANSACTION, req, NULL, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    if (reply) {
 | 
			
		||||
@@ -178,7 +179,8 @@ GBinderRemoteObject*
 | 
			
		||||
gbinder_servicemanager_hidl_get_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* fqinstance,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    /* e.g. "android.hardware.radio@1.1::IRadio/slot1" */
 | 
			
		||||
    const char* sep = strchr(fqinstance, '/');
 | 
			
		||||
@@ -193,8 +195,8 @@ gbinder_servicemanager_hidl_get_service(
 | 
			
		||||
        gbinder_local_request_append_hidl_string(req, fqname);
 | 
			
		||||
        gbinder_local_request_append_hidl_string(req, name);
 | 
			
		||||
 | 
			
		||||
        reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
            GET_TRANSACTION, req, status);
 | 
			
		||||
        reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
            GET_TRANSACTION, req, status, api);
 | 
			
		||||
 | 
			
		||||
        if (reply) {
 | 
			
		||||
            GBinderReader reader;
 | 
			
		||||
@@ -225,7 +227,8 @@ int
 | 
			
		||||
gbinder_servicemanager_hidl_add_service(
 | 
			
		||||
    GBinderServiceManager* self,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    int status;
 | 
			
		||||
    GBinderRemoteReply* reply;
 | 
			
		||||
@@ -235,8 +238,8 @@ gbinder_servicemanager_hidl_add_service(
 | 
			
		||||
    gbinder_local_request_append_hidl_string(req, name);
 | 
			
		||||
    gbinder_local_request_append_local_object(req, obj);
 | 
			
		||||
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply(self->client,
 | 
			
		||||
        ADD_TRANSACTION, req, &status);
 | 
			
		||||
    reply = gbinder_client_transact_sync_reply2(self->client,
 | 
			
		||||
        ADD_TRANSACTION, req, &status, api);
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_reply_unref(reply);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -66,12 +66,11 @@ typedef struct gbinder_servicemanager_class {
 | 
			
		||||
    const char* default_device;
 | 
			
		||||
 | 
			
		||||
    /* Methods (synchronous) */
 | 
			
		||||
    char** (*list)(GBinderServiceManager* self);
 | 
			
		||||
    GBinderRemoteObject* (*get_service)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name, int* status);
 | 
			
		||||
    int (*add_service)
 | 
			
		||||
        (GBinderServiceManager* self, const char* name,
 | 
			
		||||
            GBinderLocalObject* obj);
 | 
			
		||||
    char** (*list)(GBinderServiceManager* self, const GBinderIpcSyncApi* api);
 | 
			
		||||
    GBinderRemoteObject* (*get_service)(GBinderServiceManager* self,
 | 
			
		||||
        const char* name, int* status, const GBinderIpcSyncApi* api);
 | 
			
		||||
    int (*add_service)(GBinderServiceManager* self, const char* name,
 | 
			
		||||
        GBinderLocalObject* obj, const GBinderIpcSyncApi* api);
 | 
			
		||||
 | 
			
		||||
    /* Checking/normalizing watch names */
 | 
			
		||||
    GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -45,6 +45,7 @@ typedef struct gbinder_output_data GBinderOutputData;
 | 
			
		||||
typedef struct gbinder_rpc_protocol GBinderRpcProtocol;
 | 
			
		||||
typedef struct gbinder_servicepoll GBinderServicePoll;
 | 
			
		||||
typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
 | 
			
		||||
typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi;
 | 
			
		||||
 | 
			
		||||
#define GBINDER_INLINE_FUNC static inline
 | 
			
		||||
#define GBINDER_INTERNAL G_GNUC_INTERNAL
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,9 @@ all:
 | 
			
		||||
	@$(MAKE) -C unit_remote_reply $*
 | 
			
		||||
	@$(MAKE) -C unit_remote_request $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_aidl2 $*
 | 
			
		||||
	@$(MAKE) -C unit_servicemanager_hidl $*
 | 
			
		||||
	@$(MAKE) -C unit_servicename $*
 | 
			
		||||
	@$(MAKE) -C unit_servicepoll $*
 | 
			
		||||
	@$(MAKE) -C unit_writer $*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -14,8 +14,8 @@
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *      this software without specific prior written permission.
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
#include "gbinder_local_object.h"
 | 
			
		||||
#include "gbinder_system.h"
 | 
			
		||||
 | 
			
		||||
#define GLOG_MODULE_NAME test_binder_log
 | 
			
		||||
@@ -47,19 +49,27 @@ static GHashTable* test_fd_map = NULL;
 | 
			
		||||
static GHashTable* test_node_map = NULL;
 | 
			
		||||
static GPrivate test_looper;
 | 
			
		||||
 | 
			
		||||
#define public_fd  fd[0]
 | 
			
		||||
#define private_fd fd[1]
 | 
			
		||||
G_LOCK_DEFINE_STATIC(test_binder);
 | 
			
		||||
static GCond test_node_map_cond;
 | 
			
		||||
 | 
			
		||||
#define PUBLIC (0)
 | 
			
		||||
#define PRIVATE (1)
 | 
			
		||||
#define public_fd  node[PUBLIC].fd
 | 
			
		||||
#define private_fd node[PRIVATE].fd
 | 
			
		||||
 | 
			
		||||
#define BINDER_VERSION _IOWR('b', 9, gint32)
 | 
			
		||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, guint32)
 | 
			
		||||
 | 
			
		||||
#define B_TYPE_LARGE 0x85
 | 
			
		||||
#define BINDER_TYPE_BINDER  GBINDER_FOURCC('s', 'b', '*', B_TYPE_LARGE)
 | 
			
		||||
#define BINDER_TYPE_HANDLE  GBINDER_FOURCC('s', 'h', '*', B_TYPE_LARGE)
 | 
			
		||||
 | 
			
		||||
#define TF_ONE_WAY     0x01
 | 
			
		||||
#define TF_ROOT_OBJECT 0x04
 | 
			
		||||
#define TF_STATUS_CODE 0x08
 | 
			
		||||
#define TF_ACCEPT_FDS  0x10
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_io TestBinderIo;
 | 
			
		||||
typedef struct test_binder TestBinder;
 | 
			
		||||
 | 
			
		||||
typedef
 | 
			
		||||
void
 | 
			
		||||
@@ -76,24 +86,36 @@ typedef struct test_binder_submit_thread {
 | 
			
		||||
    TestBinder* binder;
 | 
			
		||||
} TestBinderSubmitThread;
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_node {
 | 
			
		||||
typedef struct test_binder_node TestBinderNode;
 | 
			
		||||
struct test_binder_node {
 | 
			
		||||
    int fd;
 | 
			
		||||
    char* path;
 | 
			
		||||
    int refcount;
 | 
			
		||||
    const TestBinderIo* io;
 | 
			
		||||
    TestBinder* binder;
 | 
			
		||||
    TestBinderNode* other;
 | 
			
		||||
    gboolean looper_enabled;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder_fd {
 | 
			
		||||
    int fd;
 | 
			
		||||
    GHashTable* destroy_map;
 | 
			
		||||
} TestBinderNode;
 | 
			
		||||
    TestBinderNode* node;
 | 
			
		||||
} TestBinderFd;
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder {
 | 
			
		||||
    TestBinderNode* node;
 | 
			
		||||
    gint refcount;
 | 
			
		||||
    const TestBinderIo* io;
 | 
			
		||||
    GHashTable* object_map; /* GBinderLocalObject* => handle */
 | 
			
		||||
    GHashTable* handle_map; /* handle => GBinderLocalObject* */
 | 
			
		||||
    TestBinderSubmitThread* submit_thread;
 | 
			
		||||
    gboolean looper_enabled;
 | 
			
		||||
    int fd[2];
 | 
			
		||||
    GMutex mutex;
 | 
			
		||||
    gboolean passthrough;
 | 
			
		||||
    TestBinderNode node[2];
 | 
			
		||||
} TestBinder;
 | 
			
		||||
 | 
			
		||||
struct test_binder_io {
 | 
			
		||||
    int version;
 | 
			
		||||
    int write_read_request;
 | 
			
		||||
    int (*handle_write_read)(TestBinder* binder, void* data);
 | 
			
		||||
    int (*handle_write_read)(TestBinderFd* fd, void* data);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct binder_write_read_64 {
 | 
			
		||||
@@ -118,6 +140,11 @@ typedef struct binder_transaction_data_64 {
 | 
			
		||||
    guint64 data_offsets;
 | 
			
		||||
} BinderTransactionData64;
 | 
			
		||||
 | 
			
		||||
typedef struct binder_transaction_data_sg_64 {
 | 
			
		||||
    BinderTransactionData64 tx;
 | 
			
		||||
    guint64 buffers_size;
 | 
			
		||||
} BinderTransactionDataSg64;
 | 
			
		||||
 | 
			
		||||
typedef struct binder_pre_cookie_64 {
 | 
			
		||||
    guint64 ptr;
 | 
			
		||||
    guint64 cookie;
 | 
			
		||||
@@ -128,6 +155,13 @@ typedef struct binder_handle_cookie_64 {
 | 
			
		||||
  guint64 cookie;
 | 
			
		||||
} __attribute__((packed)) BinderHandleCookie64;
 | 
			
		||||
 | 
			
		||||
typedef struct binder_object_64 {
 | 
			
		||||
  guint32 type;
 | 
			
		||||
  guint32 flags;
 | 
			
		||||
  guint64 object;
 | 
			
		||||
  guint64 cookie;
 | 
			
		||||
} BinderObject64;
 | 
			
		||||
 | 
			
		||||
#define BC_TRANSACTION_64       _IOW('c', 0, BinderTransactionData64)
 | 
			
		||||
#define BC_REPLY_64             _IOW('c', 1, BinderTransactionData64)
 | 
			
		||||
#define BC_FREE_BUFFER_64       _IOW('c', 3, guint64)
 | 
			
		||||
@@ -139,6 +173,8 @@ typedef struct binder_handle_cookie_64 {
 | 
			
		||||
#define BC_EXIT_LOOPER           _IO('c', 13)
 | 
			
		||||
#define BC_REQUEST_DEATH_NOTIFICATION_64 _IOW('c', 14, BinderHandleCookie64)
 | 
			
		||||
#define BC_CLEAR_DEATH_NOTIFICATION_64   _IOW('c', 15, BinderHandleCookie64)
 | 
			
		||||
#define BC_TRANSACTION_SG_64    _IOW('c', 17, BinderTransactionDataSg64)
 | 
			
		||||
#define BC_REPLY_SG_64          _IOW('c', 18, BinderTransactionDataSg64)
 | 
			
		||||
 | 
			
		||||
#define BR_TRANSACTION_64       _IOR('r', 2, BinderTransactionData64)
 | 
			
		||||
#define BR_REPLY_64             _IOR('r', 3, BinderTransactionData64)
 | 
			
		||||
@@ -259,74 +295,205 @@ test_binder_submit_later(
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_io_free_buffer(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    TestBinderFd* fd,
 | 
			
		||||
    void* ptr)
 | 
			
		||||
{
 | 
			
		||||
    if (ptr) {
 | 
			
		||||
        TestBinderNode* node = binder->node;
 | 
			
		||||
        GDestroyNotify destroy = g_hash_table_lookup(node->destroy_map, ptr);
 | 
			
		||||
        GDestroyNotify destroy;
 | 
			
		||||
 | 
			
		||||
        G_LOCK(test_binder);
 | 
			
		||||
        destroy = g_hash_table_lookup(fd->destroy_map, ptr);
 | 
			
		||||
        if (destroy) {
 | 
			
		||||
            g_hash_table_remove(node->destroy_map, ptr);
 | 
			
		||||
            g_hash_table_remove(fd->destroy_map, ptr);
 | 
			
		||||
            destroy(ptr);
 | 
			
		||||
        } else {
 | 
			
		||||
            g_free(ptr);
 | 
			
		||||
        }
 | 
			
		||||
        G_UNLOCK(test_binder);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_exit_wait(
 | 
			
		||||
    void)
 | 
			
		||||
{
 | 
			
		||||
    G_LOCK(test_binder);
 | 
			
		||||
    while (test_node_map) {
 | 
			
		||||
        GDEBUG("Waiting for loopers to exit...");
 | 
			
		||||
        g_cond_wait(&test_node_map_cond, &G_LOCK_NAME(test_binder));
 | 
			
		||||
    }
 | 
			
		||||
    G_UNLOCK(test_binder);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
guint64
 | 
			
		||||
test_io_passthough_fix_handle(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    guint64 handle)
 | 
			
		||||
{
 | 
			
		||||
    gpointer key = GSIZE_TO_POINTER(handle);
 | 
			
		||||
 | 
			
		||||
    /* Invoked under lock */
 | 
			
		||||
    if (g_hash_table_contains(binder->object_map, key)) {
 | 
			
		||||
        gpointer value = g_hash_table_lookup(binder->object_map, key);
 | 
			
		||||
 | 
			
		||||
        handle = GPOINTER_TO_SIZE(value);
 | 
			
		||||
        GDEBUG("Object %p => handle %u", key, (guint) handle);
 | 
			
		||||
    } else if (g_hash_table_contains(binder->handle_map, key)) {
 | 
			
		||||
        gpointer obj = g_hash_table_lookup(binder->handle_map, key);
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Handle %u => object %p", (guint) handle, obj);
 | 
			
		||||
        handle = GPOINTER_TO_SIZE(obj);
 | 
			
		||||
    }
 | 
			
		||||
    return handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gssize
 | 
			
		||||
test_io_passthough_write_64(
 | 
			
		||||
    TestBinderFd* fd,
 | 
			
		||||
    const void* bytes,
 | 
			
		||||
    gsize bytes_to_write)
 | 
			
		||||
{
 | 
			
		||||
    const guint code = *(guint32*)bytes;
 | 
			
		||||
    TestBinderNode* node = fd->node;
 | 
			
		||||
    TestBinder* binder = node->binder;
 | 
			
		||||
    BinderTransactionData64* tx = NULL;
 | 
			
		||||
    gssize bytes_written;
 | 
			
		||||
    guint extra;
 | 
			
		||||
    guint32* cmd;
 | 
			
		||||
    guint32 bytes_to_really_write;
 | 
			
		||||
    void* data;
 | 
			
		||||
 | 
			
		||||
    /* Just ignore some codes for now */
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case BC_ACQUIRE:
 | 
			
		||||
    case BC_RELEASE:
 | 
			
		||||
    case BC_REQUEST_DEATH_NOTIFICATION_64:
 | 
			
		||||
    case BC_CLEAR_DEATH_NOTIFICATION_64:
 | 
			
		||||
        return bytes_to_write;
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd = g_memdup(bytes, bytes_to_write);
 | 
			
		||||
    data = cmd + 1;
 | 
			
		||||
    switch (*cmd) {
 | 
			
		||||
    case BR_TRANSACTION_64:
 | 
			
		||||
        *cmd = BC_TRANSACTION_64;
 | 
			
		||||
        tx = data;
 | 
			
		||||
        break;
 | 
			
		||||
    case BC_TRANSACTION_64:
 | 
			
		||||
    case BC_TRANSACTION_SG_64:
 | 
			
		||||
        *cmd = BR_TRANSACTION_64;
 | 
			
		||||
         tx = data;
 | 
			
		||||
       break;
 | 
			
		||||
    case BR_REPLY_64:
 | 
			
		||||
        *cmd = BC_REPLY_64;
 | 
			
		||||
        tx = data;
 | 
			
		||||
        break;
 | 
			
		||||
    case BC_REPLY_64:
 | 
			
		||||
    case BC_REPLY_SG_64:
 | 
			
		||||
        extra = BR_TRANSACTION_COMPLETE;
 | 
			
		||||
        write(fd->fd, &extra, sizeof(extra));
 | 
			
		||||
        *cmd = BR_REPLY_64;
 | 
			
		||||
        tx = data;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (tx) {
 | 
			
		||||
        guint32* data_buffer = g_memdup(GSIZE_TO_POINTER(tx->data_buffer),
 | 
			
		||||
            tx->data_size);
 | 
			
		||||
        void* data_offsets = g_memdup(GSIZE_TO_POINTER(tx->data_offsets),
 | 
			
		||||
            tx->offsets_size);
 | 
			
		||||
 | 
			
		||||
        G_LOCK(test_binder);
 | 
			
		||||
        tx->handle = test_io_passthough_fix_handle(binder, tx->handle);
 | 
			
		||||
        if (data_buffer && tx->data_size > sizeof(BinderObject64)) {
 | 
			
		||||
            /* Replace BINDER_TYPE_BINDER with BINDER_TYPE_HANDLE */
 | 
			
		||||
            guint32* data_ptr = data_buffer;
 | 
			
		||||
            const guint32* data_end = data_buffer + ((tx->data_size -
 | 
			
		||||
                sizeof(BinderObject64))/sizeof(guint32) + 1);
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * Objects are supposed to be aligned at 32-bit boundary, so we
 | 
			
		||||
             * can scan the data buffer with 4-byte step.
 | 
			
		||||
             */
 | 
			
		||||
            for (data_ptr = data_buffer; data_ptr < data_end; data_ptr++) {
 | 
			
		||||
                if (*data_ptr == BINDER_TYPE_BINDER) {
 | 
			
		||||
                    BinderObject64* object = (BinderObject64*) data_ptr;
 | 
			
		||||
 | 
			
		||||
                    if (object->object) {
 | 
			
		||||
                        object->type = BINDER_TYPE_HANDLE;
 | 
			
		||||
                        object->object = test_io_passthough_fix_handle(binder,
 | 
			
		||||
                            object->object);
 | 
			
		||||
                    }
 | 
			
		||||
                    data_ptr += sizeof(object)/sizeof(guint32) - 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
 | 
			
		||||
        G_UNLOCK(test_binder);
 | 
			
		||||
        tx->data_buffer = GPOINTER_TO_SIZE(data_buffer);
 | 
			
		||||
        tx->data_offsets = GPOINTER_TO_SIZE(data_offsets);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Real number of bytes to write may have changed */
 | 
			
		||||
    bytes_to_really_write = sizeof(guint32) + _IOC_SIZE(*cmd);
 | 
			
		||||
    bytes_written = write(fd->fd, cmd, bytes_to_really_write);
 | 
			
		||||
    g_free(cmd);
 | 
			
		||||
    g_assert(bytes_written == bytes_to_really_write || bytes_written <= 0);
 | 
			
		||||
    return (bytes_written >= 0) ? bytes_to_write : bytes_written;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
int
 | 
			
		||||
test_io_handle_write_read_64(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    TestBinderFd* fd,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderNode* node = fd->node;
 | 
			
		||||
    TestBinder* binder = node->binder;
 | 
			
		||||
    BinderWriteRead64* wr = data;
 | 
			
		||||
    gssize bytes_left = wr->write_size - wr->write_consumed;
 | 
			
		||||
    const guint8* write_ptr = (void*)(gsize)
 | 
			
		||||
        (wr->write_buffer + wr->write_consumed);
 | 
			
		||||
    const guint8* write_ptr = (void*)(gsize)(wr->write_buffer +
 | 
			
		||||
        wr->write_consumed);
 | 
			
		||||
    gboolean is_looper;
 | 
			
		||||
 | 
			
		||||
    while (bytes_left >= sizeof(guint32)) {
 | 
			
		||||
        const guint cmd = *(guint32*)write_ptr;
 | 
			
		||||
        const guint cmdsize = _IOC_SIZE(cmd);
 | 
			
		||||
        const void* cmddata = write_ptr + sizeof(guint32);
 | 
			
		||||
        const gsize bytes_to_write = sizeof(guint32) + cmdsize;
 | 
			
		||||
 | 
			
		||||
        GASSERT(bytes_left >= (sizeof(guint32) + cmdsize));
 | 
			
		||||
        if (bytes_left >= (sizeof(guint32) + cmdsize)) {
 | 
			
		||||
            wr->write_consumed += sizeof(guint32);
 | 
			
		||||
            write_ptr += sizeof(guint32);
 | 
			
		||||
            bytes_left -= sizeof(guint32);
 | 
			
		||||
        GASSERT(bytes_left >= bytes_to_write);
 | 
			
		||||
        if (bytes_left >= bytes_to_write) {
 | 
			
		||||
            gssize bytes_written = bytes_to_write;
 | 
			
		||||
 | 
			
		||||
            switch (cmd) {
 | 
			
		||||
            case BC_TRANSACTION_64:
 | 
			
		||||
            case BC_REPLY_64:
 | 
			
		||||
                /* Is there anything special about transactions and replies? */
 | 
			
		||||
                break;
 | 
			
		||||
            case BC_FREE_BUFFER_64:
 | 
			
		||||
                test_io_free_buffer(binder,
 | 
			
		||||
                    GSIZE_TO_POINTER(*(guint64*)write_ptr));
 | 
			
		||||
                test_io_free_buffer(fd, GSIZE_TO_POINTER(*(guint64*)cmddata));
 | 
			
		||||
                break;
 | 
			
		||||
            case BC_ENTER_LOOPER:
 | 
			
		||||
                 g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
 | 
			
		||||
                 break;
 | 
			
		||||
                g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
 | 
			
		||||
                break;
 | 
			
		||||
            case BC_EXIT_LOOPER:
 | 
			
		||||
                 g_private_set(&test_looper, NULL);
 | 
			
		||||
                 break;
 | 
			
		||||
            case BC_REQUEST_DEATH_NOTIFICATION_64:
 | 
			
		||||
            case BC_CLEAR_DEATH_NOTIFICATION_64:
 | 
			
		||||
            case BC_INCREFS:
 | 
			
		||||
            case BC_ACQUIRE:
 | 
			
		||||
            case BC_RELEASE:
 | 
			
		||||
            case BC_DECREFS:
 | 
			
		||||
                g_private_set(&test_looper, NULL);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
#pragma message("TODO: implement more BINDER_WRITE_READ commands")
 | 
			
		||||
                GDEBUG("Unhandled command 0x%08x", cmd);
 | 
			
		||||
                if (binder->passthrough) {
 | 
			
		||||
                    bytes_written = test_io_passthough_write_64(fd,
 | 
			
		||||
                        write_ptr, bytes_to_write);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            wr->write_consumed += cmdsize;
 | 
			
		||||
            write_ptr += cmdsize;
 | 
			
		||||
            bytes_left -= cmdsize;
 | 
			
		||||
            if (bytes_written >= 0) {
 | 
			
		||||
                wr->write_consumed += bytes_written;
 | 
			
		||||
                write_ptr += bytes_written;
 | 
			
		||||
                bytes_left -= bytes_written;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Write failed, %s", strerror(errno));
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Partial command in the buffer */
 | 
			
		||||
            errno = EINVAL;
 | 
			
		||||
@@ -335,17 +502,20 @@ test_io_handle_write_read_64(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    is_looper = g_private_get(&test_looper) ? TRUE : FALSE;
 | 
			
		||||
    if (binder->looper_enabled || !is_looper) {
 | 
			
		||||
        /* Now read the data from the socket */
 | 
			
		||||
    if ((node->looper_enabled || !is_looper) &&
 | 
			
		||||
        (wr->read_size > wr->read_consumed)) {
 | 
			
		||||
        /* Do we have anything to read? */
 | 
			
		||||
        int bytes_available = 0;
 | 
			
		||||
        int err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
 | 
			
		||||
        int err = ioctl(fd->fd, FIONREAD, &bytes_available);
 | 
			
		||||
 | 
			
		||||
        if (err >= 0) {
 | 
			
		||||
        /* Re-check the looper_enabled flag */
 | 
			
		||||
        if (err >= 0 && (node->looper_enabled || !is_looper)) {
 | 
			
		||||
            int bytes_read = 0;
 | 
			
		||||
 | 
			
		||||
            if (bytes_available >= 4) {
 | 
			
		||||
                bytes_read = read(binder->public_fd,
 | 
			
		||||
                    (void*)(gsize)(wr->read_buffer + wr->read_consumed),
 | 
			
		||||
                /* Read the data from the socket */
 | 
			
		||||
                bytes_read = read(fd->fd, (void*)(gsize)
 | 
			
		||||
                   (wr->read_buffer + wr->read_consumed),
 | 
			
		||||
                    wr->read_size - wr->read_consumed);
 | 
			
		||||
            } else {
 | 
			
		||||
                struct timespec wait;
 | 
			
		||||
@@ -387,26 +557,26 @@ test_binder_ioctl_version(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    int* version)
 | 
			
		||||
{
 | 
			
		||||
    *version = binder->node->io->version;
 | 
			
		||||
    *version = binder->io->version;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_node_unref(
 | 
			
		||||
    TestBinderNode* node)
 | 
			
		||||
TestBinderFd*
 | 
			
		||||
test_binder_fd_from_fd(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    node->refcount--;
 | 
			
		||||
    if (!node->refcount) {
 | 
			
		||||
        g_hash_table_remove(test_node_map, node->path);
 | 
			
		||||
        g_hash_table_destroy(node->destroy_map);
 | 
			
		||||
        g_free(node->path);
 | 
			
		||||
        g_free(node);
 | 
			
		||||
    }
 | 
			
		||||
    if (!g_hash_table_size(test_node_map)) {
 | 
			
		||||
        g_hash_table_unref(test_node_map);
 | 
			
		||||
        test_node_map = NULL;
 | 
			
		||||
    TestBinderFd* binder_fd = NULL;
 | 
			
		||||
 | 
			
		||||
    G_LOCK(test_binder);
 | 
			
		||||
    GASSERT(test_fd_map);
 | 
			
		||||
    if (test_fd_map) {
 | 
			
		||||
        binder_fd = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
 | 
			
		||||
        GASSERT(binder_fd);
 | 
			
		||||
    }
 | 
			
		||||
    G_UNLOCK(test_binder);
 | 
			
		||||
 | 
			
		||||
    return binder_fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -414,13 +584,9 @@ TestBinder*
 | 
			
		||||
test_binder_from_fd(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = NULL;
 | 
			
		||||
    GASSERT(test_fd_map);
 | 
			
		||||
    if (test_fd_map) {
 | 
			
		||||
        binder = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
 | 
			
		||||
        GASSERT(binder);
 | 
			
		||||
    }
 | 
			
		||||
    return binder;
 | 
			
		||||
    TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    return binder_fd ? binder_fd->node->binder : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -435,11 +601,22 @@ void
 | 
			
		||||
test_binder_set_looper_enabled(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean enabled)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    g_assert(binder_fd);
 | 
			
		||||
    binder_fd->node->looper_enabled = enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_passthrough(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean passthrough)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    binder->looper_enabled = enabled;
 | 
			
		||||
    binder->passthrough = passthrough;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@@ -448,13 +625,13 @@ test_binder_set_destroy(
 | 
			
		||||
    gpointer ptr,
 | 
			
		||||
    GDestroyNotify destroy)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
    TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    if (binder) {
 | 
			
		||||
        TestBinderNode* node = binder->node;
 | 
			
		||||
 | 
			
		||||
        g_hash_table_replace(node->destroy_map, ptr,
 | 
			
		||||
    if (binder_fd) {
 | 
			
		||||
        G_LOCK(test_binder);
 | 
			
		||||
        g_hash_table_replace(binder_fd->destroy_map, ptr,
 | 
			
		||||
            destroy ? destroy : test_io_destroy_none);
 | 
			
		||||
        G_UNLOCK(test_binder);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -466,10 +643,10 @@ test_binder_push_data(
 | 
			
		||||
{
 | 
			
		||||
    const guint32* cmd = data;
 | 
			
		||||
    const int len = sizeof(*cmd) + _IOC_SIZE(*cmd);
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
    TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
 | 
			
		||||
    TestBinderNode* node = binder_fd->node;
 | 
			
		||||
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    g_assert(write(binder->private_fd, data, len) == len);
 | 
			
		||||
    g_assert(write(node->other->fd, data, len) == len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -704,44 +881,221 @@ test_binder_br_reply_status_later(
 | 
			
		||||
    test_binder_br_reply_status1(fd, status, test_binder_push_data_later);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_node_clear(
 | 
			
		||||
    TestBinderNode* node)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("Done with %s", node->path);
 | 
			
		||||
    g_hash_table_remove(test_node_map, node->path);
 | 
			
		||||
    if (!g_hash_table_size(test_node_map)) {
 | 
			
		||||
        g_hash_table_unref(test_node_map);
 | 
			
		||||
        g_cond_broadcast(&test_node_map_cond);
 | 
			
		||||
        test_node_map = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    close(node->fd);
 | 
			
		||||
    g_free(node->path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestBinder*
 | 
			
		||||
test_binder_ref(
 | 
			
		||||
    TestBinder* binder)
 | 
			
		||||
{
 | 
			
		||||
    if (binder) {
 | 
			
		||||
        g_atomic_int_inc(&binder->refcount);
 | 
			
		||||
    }
 | 
			
		||||
    return binder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_unregister_objects_internal(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    gboolean need_lock)
 | 
			
		||||
{
 | 
			
		||||
    GSList* objects = NULL;
 | 
			
		||||
    GHashTableIter it;
 | 
			
		||||
    gpointer value;
 | 
			
		||||
 | 
			
		||||
    if (need_lock) {
 | 
			
		||||
        G_LOCK(test_binder);
 | 
			
		||||
    }
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    g_hash_table_iter_init(&it, binder->handle_map);
 | 
			
		||||
    while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
        objects = g_slist_append(objects, value);
 | 
			
		||||
    }
 | 
			
		||||
    g_hash_table_remove_all(binder->object_map);
 | 
			
		||||
    g_hash_table_remove_all(binder->handle_map);
 | 
			
		||||
    if (need_lock) {
 | 
			
		||||
        G_UNLOCK(test_binder);
 | 
			
		||||
    }
 | 
			
		||||
    /* Unref GBinderLocalObjects outside the lock */
 | 
			
		||||
    g_slist_free_full(objects, (GDestroyNotify) gbinder_local_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_binder_unref_internal(
 | 
			
		||||
    TestBinder* binder,
 | 
			
		||||
    gboolean need_lock)
 | 
			
		||||
{
 | 
			
		||||
    if (binder && g_atomic_int_dec_and_test(&binder->refcount)) {
 | 
			
		||||
        if (need_lock) {
 | 
			
		||||
            G_LOCK(test_binder);
 | 
			
		||||
        }
 | 
			
		||||
        test_binder_node_clear(binder->node + 0);
 | 
			
		||||
        test_binder_node_clear(binder->node + 1);
 | 
			
		||||
        if (need_lock) {
 | 
			
		||||
            G_UNLOCK(test_binder);
 | 
			
		||||
        }
 | 
			
		||||
        test_binder_submit_thread_free(binder->submit_thread);
 | 
			
		||||
        test_binder_unregister_objects_internal(binder, need_lock);
 | 
			
		||||
        g_hash_table_destroy(binder->object_map);
 | 
			
		||||
        g_hash_table_destroy(binder->handle_map);
 | 
			
		||||
        g_mutex_clear(&binder->mutex);
 | 
			
		||||
        g_free(binder);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_binder_register_object(
 | 
			
		||||
    int fd,
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    guint h)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    g_assert(binder);
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
 | 
			
		||||
    G_LOCK(test_binder);
 | 
			
		||||
    g_assert(!g_hash_table_contains(binder->object_map, obj));
 | 
			
		||||
    g_assert(!g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)));
 | 
			
		||||
    if (h == AUTO_HANDLE) {
 | 
			
		||||
        h = 1;
 | 
			
		||||
        while (g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)) ||
 | 
			
		||||
            g_hash_table_contains(binder->object_map, GINT_TO_POINTER(h))) {
 | 
			
		||||
            h++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    GDEBUG("Object %p <=> handle %u", obj, h);
 | 
			
		||||
    g_hash_table_insert(binder->handle_map, GINT_TO_POINTER(h), obj);
 | 
			
		||||
    g_hash_table_insert(binder->object_map, obj, GINT_TO_POINTER(h));
 | 
			
		||||
    G_UNLOCK(test_binder);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_object_ref(obj);
 | 
			
		||||
    return h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_unregister_objects(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_unregister_objects_internal(test_binder_from_fd(fd), TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_fd_map_free(
 | 
			
		||||
    gpointer entry)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderFd* binder_fd = entry;
 | 
			
		||||
    GHashTableIter it;
 | 
			
		||||
    gpointer key, value;
 | 
			
		||||
 | 
			
		||||
    g_hash_table_iter_init(&it, binder_fd->destroy_map);
 | 
			
		||||
    while (g_hash_table_iter_next(&it, &key, &value)) {
 | 
			
		||||
        if (value) {
 | 
			
		||||
            ((GDestroyNotify)value)(key);
 | 
			
		||||
        } else {
 | 
			
		||||
            g_free(key);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    g_hash_table_destroy(binder_fd->destroy_map);
 | 
			
		||||
    test_binder_unref_internal(binder_fd->node->binder, FALSE);
 | 
			
		||||
    close(binder_fd->fd);
 | 
			
		||||
    g_free(binder_fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestBinderFd*
 | 
			
		||||
test_binder_fd_new(
 | 
			
		||||
    TestBinderNode* node)
 | 
			
		||||
{
 | 
			
		||||
    TestBinderFd* binder_fd = g_new0(TestBinderFd, 1);
 | 
			
		||||
 | 
			
		||||
    test_binder_ref(node->binder);
 | 
			
		||||
    binder_fd->node = node;
 | 
			
		||||
    binder_fd->fd = dup(node->fd);
 | 
			
		||||
    binder_fd->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
    return binder_fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
gbinder_system_open(
 | 
			
		||||
    const char* path,
 | 
			
		||||
    int flags)
 | 
			
		||||
{
 | 
			
		||||
    if (path && g_str_has_prefix(path, "/dev") &&
 | 
			
		||||
        g_str_has_suffix(path, "binder")) {
 | 
			
		||||
        TestBinderNode* node = NULL;
 | 
			
		||||
        TestBinder* binder = NULL;
 | 
			
		||||
        int fd;
 | 
			
		||||
    static const char binder_suffix[] = "binder";
 | 
			
		||||
    static const char binder_private_suffix[] = "binder-private";
 | 
			
		||||
    static const char private_suffix[] = "-private";
 | 
			
		||||
 | 
			
		||||
    if (path && g_str_has_prefix(path, "/dev/") &&
 | 
			
		||||
        (g_str_has_suffix(path, binder_suffix) ||
 | 
			
		||||
         g_str_has_suffix(path, binder_private_suffix))) {
 | 
			
		||||
        TestBinderFd* fd;
 | 
			
		||||
        TestBinderNode* node;
 | 
			
		||||
 | 
			
		||||
        G_LOCK(test_binder);
 | 
			
		||||
        node = test_node_map ? g_hash_table_lookup(test_node_map, path) : NULL;
 | 
			
		||||
        if (!node) {
 | 
			
		||||
            int i, fds[2];
 | 
			
		||||
            TestBinder* binder = g_new0(TestBinder, 1);
 | 
			
		||||
 | 
			
		||||
            binder->io = &test_io_64;
 | 
			
		||||
            g_mutex_init(&binder->mutex);
 | 
			
		||||
            g_assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
 | 
			
		||||
 | 
			
		||||
            if (g_str_has_suffix(path, binder_suffix)) {
 | 
			
		||||
                node = binder->node + PUBLIC;
 | 
			
		||||
                node->path = g_strdup(path);
 | 
			
		||||
                binder->node[PRIVATE].path = g_strconcat(path,
 | 
			
		||||
                    private_suffix, NULL);
 | 
			
		||||
            } else {
 | 
			
		||||
                node = binder->node + PRIVATE;
 | 
			
		||||
                node->path = g_strdup(path);
 | 
			
		||||
                binder->node[PUBLIC].path = g_strndup(path,
 | 
			
		||||
                    strlen(path) - strlen(private_suffix) - 1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        if (test_node_map) {
 | 
			
		||||
            node = g_hash_table_lookup(test_node_map, path);
 | 
			
		||||
        }
 | 
			
		||||
        if (node) {
 | 
			
		||||
            node->refcount++;
 | 
			
		||||
        } else {
 | 
			
		||||
            node = g_new0(TestBinderNode, 1);
 | 
			
		||||
            node->path = g_strdup(path);
 | 
			
		||||
            node->refcount = 1;
 | 
			
		||||
            node->io = &test_io_64;
 | 
			
		||||
            node->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
            if (!test_node_map) {
 | 
			
		||||
                test_node_map = g_hash_table_new(g_str_hash, g_str_equal);
 | 
			
		||||
            }
 | 
			
		||||
            g_hash_table_replace(test_node_map, node->path, node);
 | 
			
		||||
            for (i = 0; i < 2; i++) {
 | 
			
		||||
                binder->node[i].binder = binder;
 | 
			
		||||
                binder->node[i].fd = fds[i];
 | 
			
		||||
                binder->node[i].other = binder->node + ((i + 1) % 2);
 | 
			
		||||
                g_hash_table_replace(test_node_map, binder->node[i].path,
 | 
			
		||||
                    binder->node + i);
 | 
			
		||||
            }
 | 
			
		||||
            binder->object_map = g_hash_table_new
 | 
			
		||||
                (g_direct_hash, g_direct_equal);
 | 
			
		||||
            binder->handle_map = g_hash_table_new
 | 
			
		||||
                (g_direct_hash, g_direct_equal);
 | 
			
		||||
            GDEBUG("Created %s <=> %s binder", binder->node[0].path,
 | 
			
		||||
                binder->node[1].path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binder = g_new0(TestBinder, 1);
 | 
			
		||||
        binder->node = node;
 | 
			
		||||
        socketpair(AF_UNIX, SOCK_STREAM, 0, binder->fd);
 | 
			
		||||
        fd = binder->public_fd;
 | 
			
		||||
 | 
			
		||||
        fd = test_binder_fd_new(node);
 | 
			
		||||
        if (!test_fd_map) {
 | 
			
		||||
            test_fd_map = g_hash_table_new(g_direct_hash, g_direct_equal);
 | 
			
		||||
            test_fd_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
 | 
			
		||||
                NULL, test_fd_map_free);
 | 
			
		||||
        }
 | 
			
		||||
        g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd), binder);
 | 
			
		||||
        return fd;
 | 
			
		||||
        g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd->fd), fd);
 | 
			
		||||
        G_UNLOCK(test_binder);
 | 
			
		||||
 | 
			
		||||
        return fd->fd;
 | 
			
		||||
    } else {
 | 
			
		||||
        errno = ENOENT;
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -752,23 +1106,22 @@ int
 | 
			
		||||
gbinder_system_close(
 | 
			
		||||
    int fd)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (binder) {
 | 
			
		||||
        g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd));
 | 
			
		||||
    G_LOCK(test_binder);
 | 
			
		||||
    if (g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd))) {
 | 
			
		||||
        if (!g_hash_table_size(test_fd_map)) {
 | 
			
		||||
            g_hash_table_unref(test_fd_map);
 | 
			
		||||
            test_fd_map = NULL;
 | 
			
		||||
        }
 | 
			
		||||
        test_binder_submit_thread_free(binder->submit_thread);
 | 
			
		||||
        test_binder_node_unref(binder->node);
 | 
			
		||||
        close(binder->public_fd);
 | 
			
		||||
        close(binder->private_fd);
 | 
			
		||||
        g_free(binder);
 | 
			
		||||
        return 0;
 | 
			
		||||
        ret = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        errno = EBADF;
 | 
			
		||||
        ret = -1;
 | 
			
		||||
    }
 | 
			
		||||
    errno = EBADF;
 | 
			
		||||
    return -1;
 | 
			
		||||
    G_UNLOCK(test_binder);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -777,9 +1130,11 @@ gbinder_system_ioctl(
 | 
			
		||||
    int request,
 | 
			
		||||
    void* data)
 | 
			
		||||
{
 | 
			
		||||
    TestBinder* binder = test_binder_from_fd(fd);
 | 
			
		||||
    if (binder) {
 | 
			
		||||
        const TestBinderIo* io = binder->node->io;
 | 
			
		||||
    TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
 | 
			
		||||
 | 
			
		||||
    if (binder_fd) {
 | 
			
		||||
        TestBinder* binder = binder_fd->node->binder;
 | 
			
		||||
        const TestBinderIo* io = binder->io;
 | 
			
		||||
 | 
			
		||||
        switch (request) {
 | 
			
		||||
        case BINDER_VERSION:
 | 
			
		||||
@@ -788,7 +1143,7 @@ gbinder_system_ioctl(
 | 
			
		||||
            return 0;
 | 
			
		||||
        default:
 | 
			
		||||
            if (request == io->write_read_request) {
 | 
			
		||||
                return io->handle_write_read(binder, data);
 | 
			
		||||
                return io->handle_write_read(binder_fd, data);
 | 
			
		||||
            } else {
 | 
			
		||||
                errno = EINVAL;
 | 
			
		||||
                return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2019 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -14,8 +14,8 @@
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *      this software without specific prior written permission.
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
@@ -35,6 +35,8 @@
 | 
			
		||||
 | 
			
		||||
#include "test_common.h"
 | 
			
		||||
 | 
			
		||||
typedef struct test_binder TestBinder;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_br_noop(
 | 
			
		||||
    int fd);
 | 
			
		||||
@@ -116,12 +118,33 @@ test_binder_set_looper_enabled(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean enabled);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_passthrough(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gboolean passthrough);
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_binder_register_object(
 | 
			
		||||
    int fd,
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    guint handle);
 | 
			
		||||
 | 
			
		||||
#define AUTO_HANDLE ((guint)-1)
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_unregister_objects(
 | 
			
		||||
    int fd);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_set_destroy(
 | 
			
		||||
    int fd,
 | 
			
		||||
    gpointer ptr,
 | 
			
		||||
    GDestroyNotify destroy);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_binder_exit_wait(
 | 
			
		||||
    void);
 | 
			
		||||
 | 
			
		||||
#endif /* TEST_BINDER_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										421
									
								
								unit/common/test_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										421
									
								
								unit/common/test_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,421 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_servicemanager_hidl.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
#include "gbinder_cleanup.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test service manager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define SVCMGR_IFACE "android.hidl.manager@1.0::IServiceManager"
 | 
			
		||||
#define NOTIFICATION_IFACE "android.hidl.manager@1.0::IServiceNotification"
 | 
			
		||||
 | 
			
		||||
const char* const servicemanager_hidl_ifaces[] = { SVCMGR_IFACE, NULL };
 | 
			
		||||
 | 
			
		||||
enum servicemanager_hidl_tx {
 | 
			
		||||
    GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    ADD_TRANSACTION,
 | 
			
		||||
    GET_TRANSPORT_TRANSACTION,
 | 
			
		||||
    LIST_TRANSACTION,
 | 
			
		||||
    LIST_BY_INTERFACE_TRANSACTION,
 | 
			
		||||
    REGISTER_FOR_NOTIFICATIONS_TRANSACTION,
 | 
			
		||||
    DEBUG_DUMP_TRANSACTION,
 | 
			
		||||
    REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum servicemanager_hidl_notify_tx {
 | 
			
		||||
    ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass TestServiceManagerHidlClass;
 | 
			
		||||
struct test_servicemanager_hidl {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    GPtrArray* watchers;
 | 
			
		||||
    GBinderCleanup* cleanup;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define THIS_TYPE test_servicemanager_hidl_get_type()
 | 
			
		||||
#define PARENT_CLASS test_servicemanager_hidl_parent_class
 | 
			
		||||
#define THIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), THIS_TYPE, \
 | 
			
		||||
    TestServiceManagerHidl))
 | 
			
		||||
G_DEFINE_TYPE(TestServiceManagerHidl, test_servicemanager_hidl, \
 | 
			
		||||
    GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_get(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    const char* ifname;
 | 
			
		||||
    const char* instance;
 | 
			
		||||
    char* fqinstance;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert((ifname = gbinder_reader_read_hidl_string_c(&reader)));
 | 
			
		||||
    g_assert((instance = gbinder_reader_read_hidl_string_c(&reader)));
 | 
			
		||||
    fqinstance = g_strconcat(ifname, "/", instance, NULL);
 | 
			
		||||
 | 
			
		||||
    remote_obj = g_hash_table_lookup(self->objects, fqinstance);
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
    if (remote_obj) {
 | 
			
		||||
        GDEBUG("Found name '%s' => %p", fqinstance, remote_obj);
 | 
			
		||||
    } else {
 | 
			
		||||
        GDEBUG("Name '%s' not found", fqinstance);
 | 
			
		||||
    }
 | 
			
		||||
    gbinder_local_reply_append_remote_object(reply, remote_obj);
 | 
			
		||||
    g_free(fqinstance);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_add(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    const char* fqname;
 | 
			
		||||
    gboolean success;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    fqname = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    remote_obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
 | 
			
		||||
    if (fqname && remote_obj) {
 | 
			
		||||
        const char* sep = strchr(fqname, '/');
 | 
			
		||||
        GPtrArray* watchers = self->watchers;
 | 
			
		||||
        guint i;
 | 
			
		||||
 | 
			
		||||
        GDEBUG("Adding '%s'", fqname);
 | 
			
		||||
        g_hash_table_replace(self->objects, g_strdup(fqname), remote_obj);
 | 
			
		||||
 | 
			
		||||
        /* For unit test purposes, just always notify all watchers */
 | 
			
		||||
        for (i = 0; i < watchers->len; i++) {
 | 
			
		||||
            char* iface = g_strndup(fqname, sep - fqname);
 | 
			
		||||
            char* name = g_strdup(sep + 1);
 | 
			
		||||
            GBinderClient* client = watchers->pdata[i];
 | 
			
		||||
            GBinderLocalRequest* notify = gbinder_client_new_request(client);
 | 
			
		||||
            GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
            gbinder_local_request_init_writer(notify, &writer);
 | 
			
		||||
            gbinder_writer_append_hidl_string(&writer, iface);
 | 
			
		||||
            gbinder_writer_append_hidl_string(&writer, name);
 | 
			
		||||
            gbinder_writer_append_bool(&writer, FALSE);
 | 
			
		||||
 | 
			
		||||
            gbinder_writer_add_cleanup(&writer, g_free, iface);
 | 
			
		||||
            gbinder_writer_add_cleanup(&writer, g_free, name);
 | 
			
		||||
            
 | 
			
		||||
            gbinder_client_transact(client, ON_REGISTRATION_TRANSACTION,
 | 
			
		||||
                GBINDER_TX_FLAG_ONEWAY, notify, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
            /* Keep it alive longer than libgbinder needs it */
 | 
			
		||||
            self->cleanup = gbinder_cleanup_add(self->cleanup, (GDestroyNotify)
 | 
			
		||||
                gbinder_local_request_unref, notify);
 | 
			
		||||
        }
 | 
			
		||||
        success = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
        success = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_append_bool(reply, success);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_list(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GHashTableIter it;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    gpointer key;
 | 
			
		||||
    char** list = NULL;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    g_assert(gbinder_reader_at_end(&reader));
 | 
			
		||||
 | 
			
		||||
    g_hash_table_iter_init(&it, self->objects);
 | 
			
		||||
    while (g_hash_table_iter_next(&it, &key, NULL)) {
 | 
			
		||||
        list = gutil_strv_add(list, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_init_writer(reply, &writer);
 | 
			
		||||
    gbinder_writer_append_int32(&writer, 0);
 | 
			
		||||
    gbinder_writer_append_hidl_string_vec(&writer, (const char**) list, -1);
 | 
			
		||||
    gbinder_writer_add_cleanup(&writer, (GDestroyNotify) g_strfreev, list);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_register_for_notifications(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    GBinderRemoteRequest* req)
 | 
			
		||||
{
 | 
			
		||||
    GBinderRemoteObject* watcher;
 | 
			
		||||
    GBinderLocalReply* reply =
 | 
			
		||||
        gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    const char* fqname;
 | 
			
		||||
    const char* instance;
 | 
			
		||||
    gboolean success;
 | 
			
		||||
 | 
			
		||||
    gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
    fqname = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    instance = gbinder_reader_read_hidl_string_c(&reader);
 | 
			
		||||
    watcher = gbinder_reader_read_object(&reader);
 | 
			
		||||
 
 | 
			
		||||
    if (watcher) {
 | 
			
		||||
        GDEBUG("Registering watcher %s/%s", fqname, instance);
 | 
			
		||||
        g_ptr_array_add(self->watchers, gbinder_client_new(watcher,
 | 
			
		||||
            NOTIFICATION_IFACE));
 | 
			
		||||
        gbinder_remote_object_unref(watcher); /* Client keeps the reference */
 | 
			
		||||
        success = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
        success = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gbinder_local_reply_append_int32(reply, 0);
 | 
			
		||||
    gbinder_local_reply_append_bool(reply, success);
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = THIS(user_data);
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    gbinder_cleanup_reset(self->cleanup);
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GET_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_get(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case ADD_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_add(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIST_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_list(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    case REGISTER_FOR_NOTIFICATIONS_TRANSACTION:
 | 
			
		||||
        reply = test_servicemanager_hidl_register_for_notifications(self, req);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    /* If we don't defer deallocation of the reply, its buffers may get
 | 
			
		||||
     * deallocated too early. */
 | 
			
		||||
    if (reply) {
 | 
			
		||||
        self->cleanup = gbinder_cleanup_add(self->cleanup, (GDestroyNotify)
 | 
			
		||||
            gbinder_local_reply_unref, gbinder_local_reply_ref(reply));
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Interface
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_hidl_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = g_object_new(THIS_TYPE, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
 | 
			
		||||
 | 
			
		||||
    self->handle_on_looper_thread = handle_on_looper_thread;
 | 
			
		||||
    gbinder_local_object_init_base(obj, ipc, servicemanager_hidl_ifaces,
 | 
			
		||||
        test_servicemanager_hidl_handler, self);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_free(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_cleanup_reset(self->cleanup);
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
test_servicemanager_hidl_ipc(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    return self ? self->parent.ipc : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_servicemanager_hidl_object_count(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    return self ? g_hash_table_size(self->objects) : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_hidl_lookup(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    const char* name)
 | 
			
		||||
{
 | 
			
		||||
    return self ? g_hash_table_lookup(self->objects, name) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Internals
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
test_servicemanager_hidl_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
    if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
 | 
			
		||||
        return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
test_servicemanager_hidl_handle_looper_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManagerHidl* self = THIS(object);
 | 
			
		||||
 | 
			
		||||
    gbinder_cleanup_free(self->cleanup);
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    g_ptr_array_free(self->watchers, TRUE);
 | 
			
		||||
    G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_init(
 | 
			
		||||
    TestServiceManagerHidl* self)
 | 
			
		||||
{
 | 
			
		||||
    self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 | 
			
		||||
        (GDestroyNotify) gbinder_remote_object_unref);
 | 
			
		||||
    self->watchers = g_ptr_array_new_with_free_func((GDestroyNotify)
 | 
			
		||||
        gbinder_client_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_class_init(
 | 
			
		||||
    TestServiceManagerHidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = test_servicemanager_hidl_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        test_servicemanager_hidl_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        test_servicemanager_hidl_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										70
									
								
								unit/common/test_servicemanager_hidl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								unit/common/test_servicemanager_hidl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef TEST_SERVICEMANAGER_HIDL_H
 | 
			
		||||
#define TEST_SERVICEMANAGER_HIDL_H
 | 
			
		||||
 | 
			
		||||
#include <gbinder_types.h>
 | 
			
		||||
 | 
			
		||||
typedef struct test_servicemanager_hidl TestServiceManagerHidl;
 | 
			
		||||
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_hidl_new(
 | 
			
		||||
    GBinderIpc* ipc,
 | 
			
		||||
    gboolean handle_on_looper_thread);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
test_servicemanager_hidl_free(
 | 
			
		||||
    TestServiceManagerHidl* sm);
 | 
			
		||||
 | 
			
		||||
GBinderIpc*
 | 
			
		||||
test_servicemanager_hidl_ipc(
 | 
			
		||||
    TestServiceManagerHidl* self);
 | 
			
		||||
 | 
			
		||||
guint
 | 
			
		||||
test_servicemanager_hidl_object_count(
 | 
			
		||||
    TestServiceManagerHidl* self);
 | 
			
		||||
 | 
			
		||||
GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_hidl_lookup(
 | 
			
		||||
    TestServiceManagerHidl* self,
 | 
			
		||||
    const char* name);
 | 
			
		||||
 | 
			
		||||
#endif /* TEST_SERVICEMANAGER_HIDL_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
#
 | 
			
		||||
# This script requires lcov, dirname
 | 
			
		||||
@@ -21,6 +22,9 @@ unit_remote_object \
 | 
			
		||||
unit_remote_reply \
 | 
			
		||||
unit_remote_request \
 | 
			
		||||
unit_servicemanager \
 | 
			
		||||
unit_servicemanager_aidl \
 | 
			
		||||
unit_servicemanager_aidl2 \
 | 
			
		||||
unit_servicemanager_hidl \
 | 
			
		||||
unit_servicename \
 | 
			
		||||
unit_servicepoll \
 | 
			
		||||
unit_writer"
 | 
			
		||||
 
 | 
			
		||||
@@ -208,6 +208,7 @@ test_dead(
 | 
			
		||||
 | 
			
		||||
    gbinder_client_unref(client);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -84,10 +84,16 @@ test_null(
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_ref(null));
 | 
			
		||||
    gbinder_ipc_unref(null);
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert(status == (-EINVAL));
 | 
			
		||||
    g_assert(gbinder_ipc_transact_sync_oneway(null, 0, 0, NULL) == (-EINVAL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,-EINVAL);
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_worker.sync_reply(null, 0, 0, NULL, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,-EINVAL);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(null, 0, 0, NULL), == ,
 | 
			
		||||
        -EINVAL);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_worker.sync_oneway(null, 0, 0, NULL), == ,
 | 
			
		||||
        -EINVAL);
 | 
			
		||||
    g_assert(!gbinder_ipc_transact(null, 0, 0, 0, NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_custom(null, NULL, NULL, NULL, NULL));
 | 
			
		||||
    g_assert(!gbinder_ipc_object_registry(null));
 | 
			
		||||
@@ -129,6 +135,7 @@ test_basic(
 | 
			
		||||
    g_assert(!gbinder_ipc_new("invalid path"));
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -186,11 +193,11 @@ test_sync_oneway(
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
 | 
			
		||||
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    g_assert(gbinder_ipc_transact_sync_oneway(ipc, 0, 1, req) ==
 | 
			
		||||
        GBINDER_STATUS_OK);
 | 
			
		||||
    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0);
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -223,7 +230,7 @@ test_sync_reply_ok_status(
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply(fd, handle, code, data->bytes);
 | 
			
		||||
 | 
			
		||||
    tx_reply = gbinder_ipc_transact_sync_reply(ipc, handle, code, req, status);
 | 
			
		||||
    tx_reply = gbinder_ipc_sync_main.sync_reply(ipc, handle, code, req, status);
 | 
			
		||||
    g_assert(tx_reply);
 | 
			
		||||
 | 
			
		||||
    result_out = gbinder_remote_reply_read_string16(tx_reply);
 | 
			
		||||
@@ -235,6 +242,7 @@ test_sync_reply_ok_status(
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
@@ -272,12 +280,13 @@ test_sync_reply_error(
 | 
			
		||||
    test_binder_br_noop(fd);
 | 
			
		||||
    test_binder_br_reply_status(fd, expected_status);
 | 
			
		||||
 | 
			
		||||
    g_assert(!gbinder_ipc_transact_sync_reply(ipc, handle, code, req, &status));
 | 
			
		||||
    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
 | 
			
		||||
    g_assert(status == expected_status);
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -348,6 +357,7 @@ test_transact_ok(
 | 
			
		||||
    gbinder_local_reply_unref(reply);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -395,6 +405,7 @@ test_transact_dead(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -442,6 +453,7 @@ test_transact_failed(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -491,6 +503,7 @@ test_transact_status(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -523,6 +536,7 @@ test_transact_custom(
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -554,6 +568,7 @@ test_transact_custom2(
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -586,6 +601,7 @@ test_transact_custom3(
 | 
			
		||||
 | 
			
		||||
    /* Reference to GBinderIpc is released by test_transact_custom3_exec */
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -635,6 +651,7 @@ test_transact_cancel(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -680,6 +697,7 @@ test_transact_cancel2(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -769,6 +787,7 @@ test_transact_2way(
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
@@ -840,6 +859,7 @@ test_transact_incoming(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -904,6 +924,7 @@ test_transact_status_reply(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1011,6 +1032,7 @@ test_transact_async(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1084,6 +1106,7 @@ test_transact_async_sync(
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1126,6 +1149,7 @@ test_drop_remote_refs(
 | 
			
		||||
    /* gbinder_ipc_exit will drop the remote reference */
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1163,6 +1187,7 @@ test_cancel_on_exit(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -642,6 +642,7 @@ test_increfs(
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -685,6 +686,7 @@ test_decrefs(
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -726,6 +728,7 @@ test_acquire(
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -768,6 +771,7 @@ test_release(
 | 
			
		||||
    gbinder_local_object_remove_handler(obj, id);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,7 @@ test_dead(
 | 
			
		||||
    gbinder_remote_object_remove_handler(obj, 0); /* has no effect */
 | 
			
		||||
    gbinder_remote_object_unref(obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -157,7 +157,8 @@ typedef struct test_servicemanager {
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* sm)
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
@@ -169,7 +170,8 @@ GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +193,8 @@ int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(sm);
 | 
			
		||||
 | 
			
		||||
@@ -815,6 +818,7 @@ test_death(
 | 
			
		||||
    gbinder_servicemanager_remove_all_handlers(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -886,6 +890,7 @@ test_reanimate(
 | 
			
		||||
    gbinder_servicemanager_remove_all_handlers(sm, id);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager_aidl/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager_aidl/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_aidl
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										581
									
								
								unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,581 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_object.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test service manager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define SVCMGR_HANDLE (0)
 | 
			
		||||
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
 | 
			
		||||
enum servicemanager_aidl_tx {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass ServiceManagerAidlClass;
 | 
			
		||||
typedef struct service_manager_aidl {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
} ServiceManagerAidl;
 | 
			
		||||
 | 
			
		||||
#define SERVICE_MANAGER_AIDL_TYPE (service_manager_aidl_get_type())
 | 
			
		||||
#define SERVICE_MANAGER_AIDL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        SERVICE_MANAGER_AIDL_TYPE, ServiceManagerAidl))
 | 
			
		||||
G_DEFINE_TYPE(ServiceManagerAidl, service_manager_aidl, \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
servicemanager_aidl_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = user_data;
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    guint32 num;
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GET_SERVICE_TRANSACTION:
 | 
			
		||||
    case CHECK_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        if (str) {
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            remote_obj = g_hash_table_lookup(self->objects, str);
 | 
			
		||||
            if (remote_obj) {
 | 
			
		||||
                GDEBUG("Found name '%s' => %p", str, remote_obj);
 | 
			
		||||
                gbinder_local_reply_append_remote_object(reply, remote_obj);
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Name '%s' not found", str);
 | 
			
		||||
                gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
            }
 | 
			
		||||
            g_free(str);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case ADD_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        remote_obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
        if (str && remote_obj && gbinder_reader_read_uint32(&reader, &num)) {
 | 
			
		||||
            GDEBUG("Adding '%s'", str);
 | 
			
		||||
            g_hash_table_replace(self->objects, str, remote_obj);
 | 
			
		||||
            remote_obj = NULL;
 | 
			
		||||
            str = NULL;
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            *status = GBINDER_STATUS_OK;
 | 
			
		||||
        }
 | 
			
		||||
        g_free(str);
 | 
			
		||||
        gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIST_SERVICES_TRANSACTION:
 | 
			
		||||
        if (gbinder_remote_request_read_uint32(req, &num)) {
 | 
			
		||||
            if (num < g_hash_table_size(self->objects)) {
 | 
			
		||||
                GList* keys = g_hash_table_get_keys(self->objects);
 | 
			
		||||
                GList* l = g_list_nth(keys, num);
 | 
			
		||||
 | 
			
		||||
                reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
                gbinder_local_reply_append_string16(reply, l->data);
 | 
			
		||||
                g_list_free(keys);
 | 
			
		||||
                *status = GBINDER_STATUS_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Index %u out of bounds", num);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
ServiceManagerAidl*
 | 
			
		||||
servicemanager_aidl_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = g_object_new(SERVICE_MANAGER_AIDL_TYPE, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    self->handle_on_looper_thread = handle_on_looper_thread;
 | 
			
		||||
    gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
 | 
			
		||||
        servicemanager_aidl_handler, self);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_register_object(fd, obj, SVCMGR_HANDLE);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
servicemanager_aidl_free(
 | 
			
		||||
    ServiceManagerAidl* self)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
service_manager_aidl_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
 | 
			
		||||
 | 
			
		||||
    if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
 | 
			
		||||
        return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
service_manager_aidl_handle_looper_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    G_OBJECT_CLASS(service_manager_aidl_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl_init(
 | 
			
		||||
    ServiceManagerAidl* self)
 | 
			
		||||
{
 | 
			
		||||
    self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 | 
			
		||||
        (GDestroyNotify) gbinder_remote_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl_class_init(
 | 
			
		||||
    ServiceManagerAidlClass* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = service_manager_aidl_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        service_manager_aidl_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        service_manager_aidl_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    if (user_data) {
 | 
			
		||||
        g_main_loop_quit(user_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_none_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
 | 
			
		||||
        loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(g_hash_table_size(smsvc->objects), == ,1);
 | 
			
		||||
    g_assert(g_hash_table_contains(smsvc->objects, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_list {
 | 
			
		||||
    char** list;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
} TestList;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_list_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestList* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Got %u name(s)", gutil_strv_length(services));
 | 
			
		||||
    g_strfreev(test->list);
 | 
			
		||||
    test->list = services;
 | 
			
		||||
    g_main_loop_quit(test->loop);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    TestList test;
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
 | 
			
		||||
    /* Request the list and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(test.list);
 | 
			
		||||
    g_assert(!test.list[0]);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, test.loop));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(test.list[0], == ,name);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
 | 
			
		||||
    g_strfreev(test.list);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(name);
 | 
			
		||||
    GDEBUG("'%s' is registered", name);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* svc = servicemanager_aidl_new(other_dev, FALSE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_set_max_threads(ipc, 1);
 | 
			
		||||
 | 
			
		||||
    /* Start watching */
 | 
			
		||||
    id = gbinder_servicemanager_add_registration_handler(sm, name,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    /* Register the object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, NULL));
 | 
			
		||||
 | 
			
		||||
    /* test_notify_cb will stop the loop */
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(svc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify2
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify2()
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, TRUE);
 | 
			
		||||
    GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name1 = "name1";
 | 
			
		||||
    const char* name2 = "name2";
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    gulong id1, id2;
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_set_max_threads(ipc, 1);
 | 
			
		||||
 | 
			
		||||
    /* Register the object synchronously (twice)*/
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name1, obj);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name1,obj),==,0);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(sm, name1, NULL));
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name2, obj);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name2,obj),==,0);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(sm, name2, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Watch for the first name to create internal name watcher */
 | 
			
		||||
    id1 = gbinder_servicemanager_add_registration_handler(sm, name1,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id1);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Now watch for the second name */
 | 
			
		||||
    id2 = gbinder_servicemanager_add_registration_handler(sm, name2,
 | 
			
		||||
        test_notify_cb, loop);
 | 
			
		||||
    g_assert(id2);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id1);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id2);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    servicemanager_aidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_aidl/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    g_test_add_func(TEST_("notify"), test_notify);
 | 
			
		||||
    g_test_add_func(TEST_("notify2"), test_notify2);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										5
									
								
								unit/unit_servicemanager_aidl2/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								unit/unit_servicemanager_aidl2/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_aidl2
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										437
									
								
								unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,437 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_rpc_protocol.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_object.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] =
 | 
			
		||||
    "gbinder-test-servicemanager_aidl2-XXXXXX";
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_hidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_hidl object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test service manager
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define SVCMGR_HANDLE (0)
 | 
			
		||||
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
 | 
			
		||||
enum servicemanager_aidl_tx {
 | 
			
		||||
    GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
 | 
			
		||||
    CHECK_SERVICE_TRANSACTION,
 | 
			
		||||
    ADD_SERVICE_TRANSACTION,
 | 
			
		||||
    LIST_SERVICES_TRANSACTION
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
 | 
			
		||||
 | 
			
		||||
typedef GBinderLocalObjectClass ServiceManagerAidl2Class;
 | 
			
		||||
typedef struct service_manager_aidl2 {
 | 
			
		||||
    GBinderLocalObject parent;
 | 
			
		||||
    GHashTable* objects;
 | 
			
		||||
    gboolean handle_on_looper_thread;
 | 
			
		||||
} ServiceManagerAidl2;
 | 
			
		||||
 | 
			
		||||
#define SERVICE_MANAGER_AIDL2_TYPE (service_manager_aidl2_get_type())
 | 
			
		||||
#define SERVICE_MANAGER_AIDL2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
 | 
			
		||||
        SERVICE_MANAGER_AIDL2_TYPE, ServiceManagerAidl2))
 | 
			
		||||
G_DEFINE_TYPE(ServiceManagerAidl2, service_manager_aidl2, \
 | 
			
		||||
        GBINDER_TYPE_LOCAL_OBJECT)
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
servicemanager_aidl2_handler(
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = user_data;
 | 
			
		||||
    GBinderLocalReply* reply = NULL;
 | 
			
		||||
    GBinderReader reader;
 | 
			
		||||
    GBinderRemoteObject* remote_obj;
 | 
			
		||||
    guint32 num, allow_isolated, dumpsys_priority;
 | 
			
		||||
    char* str;
 | 
			
		||||
 | 
			
		||||
    g_assert(!flags);
 | 
			
		||||
    GDEBUG("%s %u", gbinder_remote_request_interface(req), code);
 | 
			
		||||
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
 | 
			
		||||
    *status = -1;
 | 
			
		||||
    switch (code) {
 | 
			
		||||
    case GET_SERVICE_TRANSACTION:
 | 
			
		||||
    case CHECK_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        if (str) {
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            remote_obj = g_hash_table_lookup(self->objects, str);
 | 
			
		||||
            if (remote_obj) {
 | 
			
		||||
                GDEBUG("Found name '%s' => %p", str, remote_obj);
 | 
			
		||||
                gbinder_local_reply_append_remote_object(reply, remote_obj);
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Name '%s' not found", str);
 | 
			
		||||
                gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
 | 
			
		||||
            }
 | 
			
		||||
            g_free(str);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case ADD_SERVICE_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        str = gbinder_reader_read_string16(&reader);
 | 
			
		||||
        remote_obj = gbinder_reader_read_object(&reader);
 | 
			
		||||
        if (str && remote_obj &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &allow_isolated) &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
 | 
			
		||||
            GDEBUG("Adding '%s'", str);
 | 
			
		||||
            g_hash_table_replace(self->objects, str, remote_obj);
 | 
			
		||||
            remote_obj = NULL;
 | 
			
		||||
            str = NULL;
 | 
			
		||||
            reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
            *status = GBINDER_STATUS_OK;
 | 
			
		||||
        }
 | 
			
		||||
        g_free(str);
 | 
			
		||||
        gbinder_remote_object_unref(remote_obj);
 | 
			
		||||
        break;
 | 
			
		||||
    case LIST_SERVICES_TRANSACTION:
 | 
			
		||||
        gbinder_remote_request_init_reader(req, &reader);
 | 
			
		||||
        if (gbinder_reader_read_uint32(&reader, &num) &&
 | 
			
		||||
            gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
 | 
			
		||||
            if (num < g_hash_table_size(self->objects)) {
 | 
			
		||||
                GList* keys = g_hash_table_get_keys(self->objects);
 | 
			
		||||
                GList* l = g_list_nth(keys, num);
 | 
			
		||||
 | 
			
		||||
                /* Ignore dumpsys_priority */
 | 
			
		||||
                reply = gbinder_local_object_new_reply(obj);
 | 
			
		||||
                gbinder_local_reply_append_string16(reply, l->data);
 | 
			
		||||
                g_list_free(keys);
 | 
			
		||||
                *status = GBINDER_STATUS_OK;
 | 
			
		||||
            } else {
 | 
			
		||||
                GDEBUG("Index %u out of bounds", num);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        GDEBUG("Unhandled command %u", code);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
ServiceManagerAidl2*
 | 
			
		||||
servicemanager_aidl2_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = g_object_new(SERVICE_MANAGER_AIDL2_TYPE, NULL);
 | 
			
		||||
    GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    self->handle_on_looper_thread = handle_on_looper_thread;
 | 
			
		||||
    gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
 | 
			
		||||
        servicemanager_aidl2_handler, self);
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_register_object(fd, obj, SVCMGR_HANDLE);
 | 
			
		||||
    gbinder_ipc_register_local_object(ipc, obj);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBINDER_LOCAL_TRANSACTION_SUPPORT
 | 
			
		||||
service_manager_aidl2_can_handle_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    const char* iface,
 | 
			
		||||
    guint code)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
 | 
			
		||||
 | 
			
		||||
    if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
 | 
			
		||||
        return GBINDER_LOCAL_TRANSACTION_LOOPER;
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
 | 
			
		||||
            can_handle_transaction(object, iface, code);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
GBinderLocalReply*
 | 
			
		||||
service_manager_aidl2_handle_looper_transaction(
 | 
			
		||||
    GBinderLocalObject* object,
 | 
			
		||||
    GBinderRemoteRequest* req,
 | 
			
		||||
    guint code,
 | 
			
		||||
    guint flags,
 | 
			
		||||
    int* status)
 | 
			
		||||
{
 | 
			
		||||
    if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
 | 
			
		||||
            handle_transaction(object, req, code, flags, status);
 | 
			
		||||
    } else {
 | 
			
		||||
        return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
 | 
			
		||||
            handle_looper_transaction(object, req, code, flags, status);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl2_finalize(
 | 
			
		||||
    GObject* object)
 | 
			
		||||
{
 | 
			
		||||
    ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
 | 
			
		||||
 | 
			
		||||
    g_hash_table_destroy(self->objects);
 | 
			
		||||
    G_OBJECT_CLASS(service_manager_aidl2_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl2_init(
 | 
			
		||||
    ServiceManagerAidl2* self)
 | 
			
		||||
{
 | 
			
		||||
    self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
 | 
			
		||||
        (GDestroyNotify) gbinder_remote_object_unref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
service_manager_aidl2_class_init(
 | 
			
		||||
    ServiceManagerAidl2Class* klass)
 | 
			
		||||
{
 | 
			
		||||
    GObjectClass* object = G_OBJECT_CLASS(klass);
 | 
			
		||||
    GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    object->finalize = service_manager_aidl2_finalize;
 | 
			
		||||
    local_object->can_handle_transaction =
 | 
			
		||||
        service_manager_aidl2_can_handle_transaction;
 | 
			
		||||
    local_object->handle_looper_transaction =
 | 
			
		||||
        service_manager_aidl2_handle_looper_transaction;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Test context
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_context {
 | 
			
		||||
    const char* default_config_dir;
 | 
			
		||||
    const char* default_config_file;
 | 
			
		||||
    char* config_dir;
 | 
			
		||||
    char* config_subdir;
 | 
			
		||||
    char* config_file;
 | 
			
		||||
    GBinderLocalObject* object;
 | 
			
		||||
    ServiceManagerAidl2* service;
 | 
			
		||||
    GBinderServiceManager* client;
 | 
			
		||||
    int fd;
 | 
			
		||||
} TestContext;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_context_init(
 | 
			
		||||
    TestContext* test)
 | 
			
		||||
{
 | 
			
		||||
    const char* dev = GBINDER_DEFAULT_BINDER;
 | 
			
		||||
    const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
 | 
			
		||||
    /*
 | 
			
		||||
     * Also set defaults so that both /dev/binder and /dev/binder-private
 | 
			
		||||
     * use the same protocol.
 | 
			
		||||
     */
 | 
			
		||||
    const char* config =
 | 
			
		||||
        "[Protocol]\n"
 | 
			
		||||
        "Default = aidl2\n"
 | 
			
		||||
        "/dev/binder = aidl2\n"
 | 
			
		||||
        "[ServiceManager]\n"
 | 
			
		||||
        "Default = aidl2\n"
 | 
			
		||||
        "/dev/binder = aidl2\n";
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
 | 
			
		||||
    memset(test, 0, sizeof(*test));
 | 
			
		||||
    test->default_config_dir = gbinder_config_dir;
 | 
			
		||||
    test->default_config_file = gbinder_config_file;
 | 
			
		||||
    test->config_dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    test->config_subdir = g_build_filename(test->config_dir, "d", NULL);
 | 
			
		||||
    test->config_file = g_build_filename(test->config_dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(test->config_file, config, -1, NULL));
 | 
			
		||||
    GDEBUG("Config file %s", test->config_file);
 | 
			
		||||
    gbinder_config_dir = test->config_subdir; /* Doesn't exist */
 | 
			
		||||
    gbinder_config_file = test->config_file;
 | 
			
		||||
 | 
			
		||||
    ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    test->fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(test->fd, test->object, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(test->fd, TRUE);
 | 
			
		||||
 | 
			
		||||
    test->service = servicemanager_aidl2_new(other_dev, TRUE);
 | 
			
		||||
    test->client = gbinder_servicemanager_new(dev);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_context_deinit(
 | 
			
		||||
    TestContext* test)
 | 
			
		||||
{
 | 
			
		||||
    test_binder_unregister_objects(test->fd);
 | 
			
		||||
    gbinder_local_object_unref(test->object);
 | 
			
		||||
    gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(test->service));
 | 
			
		||||
    gbinder_servicemanager_unref(test->client);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    remove(test->config_file);
 | 
			
		||||
    remove(test->config_dir);
 | 
			
		||||
    g_free(test->config_file);
 | 
			
		||||
    g_free(test->config_subdir);
 | 
			
		||||
    g_free(test->config_dir);
 | 
			
		||||
    gbinder_config_dir = test->default_config_dir;
 | 
			
		||||
    gbinder_config_file = test->default_config_file;
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    TestContext test;
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    int status = -1;
 | 
			
		||||
 | 
			
		||||
    test_context_init(&test);
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(!gbinder_servicemanager_get_service_sync(test.client,
 | 
			
		||||
        name, &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    /* Register object */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, test.object);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
 | 
			
		||||
        name, test.object), == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1);
 | 
			
		||||
    g_assert(g_hash_table_contains(test.service->objects, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service_sync(test.client, name,
 | 
			
		||||
        &status));
 | 
			
		||||
    g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    test_context_deinit(&test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    TestContext test;
 | 
			
		||||
    const char* name = "name";
 | 
			
		||||
    char** list;
 | 
			
		||||
 | 
			
		||||
    test_context_init(&test);
 | 
			
		||||
 | 
			
		||||
    /* Request the list */
 | 
			
		||||
    list = gbinder_servicemanager_list_sync(test.client);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(list);
 | 
			
		||||
    g_assert(!list[0]);
 | 
			
		||||
    g_strfreev(list);
 | 
			
		||||
 | 
			
		||||
    /* Register object */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, test.object);
 | 
			
		||||
    g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
 | 
			
		||||
        name, test.object), == ,GBINDER_STATUS_OK);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    list = gbinder_servicemanager_list_sync(test.client);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(list[0], == ,name);
 | 
			
		||||
    g_strfreev(list);
 | 
			
		||||
 | 
			
		||||
    test_context_deinit(&test);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_aidl2/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										6
									
								
								unit/unit_servicemanager_hidl/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								unit/unit_servicemanager_hidl/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# -*- Mode: makefile-gmake -*-
 | 
			
		||||
 | 
			
		||||
EXE = unit_servicemanager_hidl
 | 
			
		||||
COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c
 | 
			
		||||
 | 
			
		||||
include ../common/Makefile
 | 
			
		||||
							
								
								
									
										470
									
								
								unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,470 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 *
 | 
			
		||||
 *   1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *      documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *   3. Neither the names of the copyright holders nor the names of its
 | 
			
		||||
 *      contributors may be used to endorse or promote products derived
 | 
			
		||||
 *      from this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
			
		||||
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
			
		||||
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "test_binder.h"
 | 
			
		||||
#include "test_servicemanager_hidl.h"
 | 
			
		||||
 | 
			
		||||
#include "gbinder_ipc.h"
 | 
			
		||||
#include "gbinder_cleanup.h"
 | 
			
		||||
#include "gbinder_config.h"
 | 
			
		||||
#include "gbinder_client_p.h"
 | 
			
		||||
#include "gbinder_driver.h"
 | 
			
		||||
#include "gbinder_reader.h"
 | 
			
		||||
#include "gbinder_writer.h"
 | 
			
		||||
#include "gbinder_servicemanager_p.h"
 | 
			
		||||
#include "gbinder_local_object_p.h"
 | 
			
		||||
#include "gbinder_local_reply.h"
 | 
			
		||||
#include "gbinder_local_request.h"
 | 
			
		||||
#include "gbinder_remote_request.h"
 | 
			
		||||
#include "gbinder_remote_object_p.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_log.h>
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
static TestOpt test_opt;
 | 
			
		||||
#define MAIN_DEV GBINDER_DEFAULT_HWBINDER
 | 
			
		||||
#define OTHER_DEV GBINDER_DEFAULT_HWBINDER "-private"
 | 
			
		||||
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-svcmgr-hidl-XXXXXX";
 | 
			
		||||
static const char DEFAULT_CONFIG_DATA[] =
 | 
			
		||||
    "[Protocol]\n"
 | 
			
		||||
    MAIN_DEV " = hidl\n"
 | 
			
		||||
    OTHER_DEV " = hidl\n"
 | 
			
		||||
    "[ServiceManager]\n"
 | 
			
		||||
    MAIN_DEV " = hidl\n";
 | 
			
		||||
 | 
			
		||||
typedef struct test_config {
 | 
			
		||||
    char* dir;
 | 
			
		||||
    char* file;
 | 
			
		||||
} TestConfig;
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GType
 | 
			
		||||
gbinder_servicemanager_aidl2_get_type()
 | 
			
		||||
{
 | 
			
		||||
    /* Avoid pulling in gbinder_servicemanager_aidl2 object */
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_init(
 | 
			
		||||
    TestConfig* config,
 | 
			
		||||
    char* config_data)
 | 
			
		||||
{
 | 
			
		||||
    config->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
 | 
			
		||||
    config->file = g_build_filename(config->dir, "test.conf", NULL);
 | 
			
		||||
    g_assert(g_file_set_contents(config->file, config_data ? config_data :
 | 
			
		||||
        DEFAULT_CONFIG_DATA, -1, NULL));
 | 
			
		||||
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
    gbinder_config_dir = config->dir;
 | 
			
		||||
    gbinder_config_file = config->file;
 | 
			
		||||
    GDEBUG("Wrote config to %s", config->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_config_deinit(
 | 
			
		||||
    TestConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    gbinder_config_exit();
 | 
			
		||||
 | 
			
		||||
    remove(config->file);
 | 
			
		||||
    g_free(config->file);
 | 
			
		||||
 | 
			
		||||
    remove(config->dir);
 | 
			
		||||
    g_free(config->dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
TestServiceManagerHidl*
 | 
			
		||||
test_servicemanager_impl_new(
 | 
			
		||||
    const char* dev,
 | 
			
		||||
    gboolean handle_on_looper_thread)
 | 
			
		||||
{
 | 
			
		||||
    GBinderIpc* ipc = gbinder_ipc_new(dev);
 | 
			
		||||
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    TestServiceManagerHidl* sm =
 | 
			
		||||
        test_servicemanager_hidl_new(ipc, handle_on_looper_thread);
 | 
			
		||||
 | 
			
		||||
    test_binder_set_looper_enabled(fd, TRUE);
 | 
			
		||||
    test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm),
 | 
			
		||||
        GBINDER_SERVICEMANAGER_HANDLE);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    return sm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * get
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    GDEBUG("Name added");
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    if (user_data) {
 | 
			
		||||
        g_main_loop_quit(user_data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_none_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(!obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    GBinderRemoteObject* obj,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(obj);
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_main_loop_quit(user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_get()
 | 
			
		||||
{
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    smsvc = test_servicemanager_impl_new(OTHER_DEV, FALSE);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* This one fails because of unexpected name format */
 | 
			
		||||
    g_assert(!gbinder_servicemanager_get_service_sync(sm, "test", NULL));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (it's not there yet) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s'", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
 | 
			
		||||
        loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    g_assert_cmpuint(test_servicemanager_hidl_object_count(smsvc), == ,1);
 | 
			
		||||
    g_assert(test_servicemanager_hidl_lookup(smsvc, name));
 | 
			
		||||
 | 
			
		||||
    /* Query the object (this time it must be there) and wait for completion */
 | 
			
		||||
    GDEBUG("Querying '%s' again", name);
 | 
			
		||||
    g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * list
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_list {
 | 
			
		||||
    char** list;
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
} TestList;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
gboolean
 | 
			
		||||
test_list_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    char** services,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestList* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Got %u name(s)", gutil_strv_length(services));
 | 
			
		||||
    g_strfreev(test->list);
 | 
			
		||||
    test->list = services;
 | 
			
		||||
    g_main_loop_quit(test->loop);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_list()
 | 
			
		||||
{
 | 
			
		||||
    TestList test;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    smsvc = test_servicemanager_impl_new(OTHER_DEV, FALSE);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* Request the list and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* There's nothing there yet */
 | 
			
		||||
    g_assert(test.list);
 | 
			
		||||
    g_assert(!test.list[0]);
 | 
			
		||||
 | 
			
		||||
    /* Register object and wait for completion */
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_add_cb, test.loop));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Request the list again */
 | 
			
		||||
    g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
 | 
			
		||||
    /* Now the name must be there */
 | 
			
		||||
    g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
 | 
			
		||||
    g_assert_cmpstr(test.list[0], == ,name);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
 | 
			
		||||
    g_strfreev(test.list);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * notify
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_notify {
 | 
			
		||||
    GMainLoop* loop;
 | 
			
		||||
    TestServiceManagerHidl* smsvc;
 | 
			
		||||
    int notify_count;
 | 
			
		||||
    gboolean name_added;
 | 
			
		||||
} TestNotify;
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_never(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    g_assert_not_reached();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_add_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    int status,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestNotify* test = user_data;
 | 
			
		||||
 | 
			
		||||
    GDEBUG("Name added");
 | 
			
		||||
    g_assert(status == GBINDER_STATUS_OK);
 | 
			
		||||
    g_assert(!test->name_added);
 | 
			
		||||
    test->name_added = TRUE;
 | 
			
		||||
    if (test->notify_count) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify_cb(
 | 
			
		||||
    GBinderServiceManager* sm,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    void* user_data)
 | 
			
		||||
{
 | 
			
		||||
    TestNotify* test = user_data;
 | 
			
		||||
    GBinderIpc* ipc = test_servicemanager_hidl_ipc(test->smsvc);
 | 
			
		||||
    int fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
    
 | 
			
		||||
    g_assert(name);
 | 
			
		||||
    GDEBUG("'%s' is registered", name);
 | 
			
		||||
    g_assert_cmpint(test->notify_count, == ,0);
 | 
			
		||||
    test->notify_count++;
 | 
			
		||||
    /* We want BR_TRANSACTION_COMPLETE to be handled by the transaction
 | 
			
		||||
     * thread, disable the looper before pushing the data. */
 | 
			
		||||
    test_binder_set_looper_enabled(fd, FALSE);
 | 
			
		||||
    test_binder_br_transaction_complete(fd);
 | 
			
		||||
    /* Exit the loop after both things happen */
 | 
			
		||||
    if (test->name_added) {
 | 
			
		||||
        g_main_loop_quit(test->loop);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_notify()
 | 
			
		||||
{
 | 
			
		||||
    TestNotify test;
 | 
			
		||||
    TestConfig config;
 | 
			
		||||
    GBinderIpc* ipc;
 | 
			
		||||
    GBinderLocalObject* obj;
 | 
			
		||||
    int fd;
 | 
			
		||||
    GBinderServiceManager* sm;
 | 
			
		||||
    const char* name = "android.hidl.base@1.0::IBase/test";
 | 
			
		||||
    gulong id;
 | 
			
		||||
 | 
			
		||||
    memset(&test, 0, sizeof(test));
 | 
			
		||||
    test.loop = g_main_loop_new(NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
    test_config_init(&config, NULL);
 | 
			
		||||
    ipc = gbinder_ipc_new(MAIN_DEV);
 | 
			
		||||
    test.smsvc = test_servicemanager_impl_new(OTHER_DEV, TRUE);
 | 
			
		||||
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
			
		||||
    fd = gbinder_driver_fd(ipc->driver);
 | 
			
		||||
 | 
			
		||||
    /* Set up binder simulator */
 | 
			
		||||
    test_binder_register_object(fd, obj, AUTO_HANDLE);
 | 
			
		||||
    test_binder_set_passthrough(fd, TRUE);
 | 
			
		||||
    sm = gbinder_servicemanager_new(MAIN_DEV);
 | 
			
		||||
 | 
			
		||||
    /* This one fails because of invalid names */
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL,
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, "",
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
    g_assert(!gbinder_servicemanager_add_registration_handler(sm, ",",
 | 
			
		||||
        test_notify_never, NULL));
 | 
			
		||||
 | 
			
		||||
    /* Start watching */
 | 
			
		||||
    id = gbinder_servicemanager_add_registration_handler(sm, name,
 | 
			
		||||
        test_notify_cb, &test);
 | 
			
		||||
    g_assert(id);
 | 
			
		||||
 | 
			
		||||
    /* Register the object and wait for completion */
 | 
			
		||||
    GDEBUG("Registering object '%s' => %p", name, obj);
 | 
			
		||||
    g_assert(gbinder_servicemanager_add_service(sm, name, obj,
 | 
			
		||||
        test_notify_add_cb, &test));
 | 
			
		||||
 | 
			
		||||
    /* The loop quits after the name is added and notification is received */
 | 
			
		||||
    test_run(&test_opt, test.loop);
 | 
			
		||||
    gbinder_servicemanager_remove_handler(sm, id);
 | 
			
		||||
 | 
			
		||||
    test_binder_unregister_objects(fd);
 | 
			
		||||
    gbinder_local_object_unref(obj);
 | 
			
		||||
    test_servicemanager_hidl_free(test.smsvc);
 | 
			
		||||
    gbinder_servicemanager_unref(sm);
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    test_binder_exit_wait();
 | 
			
		||||
    test_config_deinit(&config);
 | 
			
		||||
    g_main_loop_unref(test.loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * Common
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_(t) "/servicemanager_hidl/" t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_("get"), test_get);
 | 
			
		||||
    g_test_add_func(TEST_("list"), test_list);
 | 
			
		||||
    g_test_add_func(TEST_("notify"), test_notify);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 4
 | 
			
		||||
 * indent-tabs-mode: nil
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2019-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2019-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2019-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -101,7 +101,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    char** ret;
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
@@ -118,7 +119,8 @@ GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    *status = (-ENOENT);
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -129,7 +131,8 @@ int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
@@ -345,6 +348,7 @@ test_present(
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -474,6 +478,7 @@ test_cancel(
 | 
			
		||||
    gbinder_ipc_unref(ipc);
 | 
			
		||||
    test_run(&test_opt, loop);
 | 
			
		||||
 | 
			
		||||
    gbinder_ipc_exit();
 | 
			
		||||
    g_main_loop_unref(loop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2018-2020 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 * Copyright (C) 2018-2021 Jolla Ltd.
 | 
			
		||||
 * Copyright (C) 2018-2021 Slava Monich <slava.monich@jolla.com>
 | 
			
		||||
 *
 | 
			
		||||
 * You may use this file under the terms of BSD license as follows:
 | 
			
		||||
 *
 | 
			
		||||
@@ -79,7 +79,8 @@ G_DEFINE_TYPE(TestServiceManager, test_servicemanager,
 | 
			
		||||
static
 | 
			
		||||
char**
 | 
			
		||||
test_servicemanager_list(
 | 
			
		||||
    GBinderServiceManager* manager)
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    char** ret;
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
@@ -96,7 +97,8 @@ GBinderRemoteObject*
 | 
			
		||||
test_servicemanager_get_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    int* status)
 | 
			
		||||
    int* status,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    *status = (-ENOENT);
 | 
			
		||||
    return NULL;
 | 
			
		||||
@@ -107,7 +109,8 @@ int
 | 
			
		||||
test_servicemanager_add_service(
 | 
			
		||||
    GBinderServiceManager* manager,
 | 
			
		||||
    const char* name,
 | 
			
		||||
    GBinderLocalObject* obj)
 | 
			
		||||
    GBinderLocalObject* obj,
 | 
			
		||||
    const GBinderIpcSyncApi* api)
 | 
			
		||||
{
 | 
			
		||||
    TestServiceManager* self = TEST_SERVICEMANAGER(manager);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user