1395 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1395 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2018-2024 Slava Monich <slava@monich.com>
 | 
						|
 * Copyright (C) 2018-2022 Jolla Ltd.
 | 
						|
 *
 | 
						|
 * You may use this file under the terms of BSD license as follows:
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 *
 | 
						|
 *   1. Redistributions of source code must retain the above copyright
 | 
						|
 *      notice, this list of conditions and the following disclaimer.
 | 
						|
 *   2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *      notice, this list of conditions and the following disclaimer in the
 | 
						|
 *      documentation and/or other materials provided with the distribution.
 | 
						|
 *   3. Neither the names of the copyright holders nor the names of its
 | 
						|
 *      contributors may be used to endorse or promote products derived
 | 
						|
 *      from this software without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
						|
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 | 
						|
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
						|
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
						|
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
						|
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
						|
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
						|
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 | 
						|
 * THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include "test_binder.h"
 | 
						|
 | 
						|
#include "gbinder_ipc.h"
 | 
						|
#include "gbinder_driver.h"
 | 
						|
#include "gbinder_local_object_p.h"
 | 
						|
#include "gbinder_local_reply_p.h"
 | 
						|
#include "gbinder_local_request_p.h"
 | 
						|
#include "gbinder_object_registry.h"
 | 
						|
#include "gbinder_output_data.h"
 | 
						|
#include "gbinder_remote_reply.h"
 | 
						|
#include "gbinder_remote_request.h"
 | 
						|
#include "gbinder_rpc_protocol.h"
 | 
						|
#include "gbinder_writer.h"
 | 
						|
 | 
						|
#include <gutil_log.h>
 | 
						|
 | 
						|
#include <unistd.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
static TestOpt test_opt;
 | 
						|
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-ipc-XXXXXX";
 | 
						|
 | 
						|
static
 | 
						|
gboolean
 | 
						|
test_unref_ipc(
 | 
						|
    gpointer ipc)
 | 
						|
{
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    return G_SOURCE_REMOVE;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_quit_when_destroyed(
 | 
						|
    gpointer loop,
 | 
						|
    GObject* obj)
 | 
						|
{
 | 
						|
    test_quit_later((GMainLoop*)loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalRequest*
 | 
						|
test_local_request_new(
 | 
						|
    GBinderIpc* ipc)
 | 
						|
{
 | 
						|
    return gbinder_local_request_new(gbinder_driver_io(ipc->driver),
 | 
						|
        gbinder_driver_protocol(ipc->driver), NULL);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_local_reply_new(
 | 
						|
    GBinderIpc* ipc)
 | 
						|
{
 | 
						|
    return gbinder_local_reply_new(gbinder_driver_io(ipc->driver),
 | 
						|
        gbinder_driver_protocol(ipc->driver));
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * null
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_null(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    int status = INT_MAX;
 | 
						|
 | 
						|
    g_assert(!gbinder_ipc_ref(NULL));
 | 
						|
    gbinder_ipc_unref(NULL);
 | 
						|
    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));
 | 
						|
    gbinder_ipc_looper_check(NULL);
 | 
						|
    gbinder_ipc_cancel(NULL, 0);
 | 
						|
 | 
						|
    g_assert(!gbinder_object_registry_ref(NULL));
 | 
						|
    gbinder_object_registry_unref(NULL);
 | 
						|
    g_assert(!gbinder_object_registry_get_local(NULL, NULL));
 | 
						|
    g_assert(!gbinder_object_registry_get_remote(NULL, 0, FALSE));
 | 
						|
    g_assert(!gbinder_ipc_find_local_object(NULL, NULL, NULL));
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * basic
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
gboolean
 | 
						|
test_basic_find_none(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
gboolean
 | 
						|
test_basic_find(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    return obj == user_data;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_basic(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
 | 
						|
    GBinderLocalObject* obj;
 | 
						|
 | 
						|
    g_assert(ipc);
 | 
						|
    g_assert(ipc2);
 | 
						|
    g_assert(ipc != ipc2);
 | 
						|
    gbinder_ipc_cancel(ipc2, 0); /* not a valid transaction */
 | 
						|
    gbinder_ipc_unref(ipc2);
 | 
						|
 | 
						|
    g_assert(!gbinder_ipc_find_local_object(NULL, test_basic_find_none, NULL));
 | 
						|
    g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL));
 | 
						|
    obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
 | 
						|
    g_assert(obj);
 | 
						|
    g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL));
 | 
						|
    g_assert(gbinder_ipc_find_local_object(ipc, test_basic_find, obj) == obj);
 | 
						|
    gbinder_local_object_unref(obj); /* Above call added a reference */
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
 | 
						|
    /* Second gbinder_ipc_new returns the same (default) object */
 | 
						|
    g_assert(gbinder_ipc_new(NULL, NULL) == ipc);
 | 
						|
    g_assert(gbinder_ipc_new("", NULL) == ipc);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
 | 
						|
    /* Invalid path */
 | 
						|
    g_assert(!gbinder_ipc_new("invalid path", NULL));
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * protocol
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_protocol(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    /* GBinderIpc objects are identified by device + protocol combination */
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, "aidl");
 | 
						|
    GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, "hidl");
 | 
						|
 | 
						|
    g_assert(ipc);
 | 
						|
    g_assert(ipc2);
 | 
						|
    g_assert(ipc != ipc2);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    gbinder_ipc_unref(ipc2);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * async_oneway
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_async_oneway_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    g_assert(!status);
 | 
						|
    g_assert(!reply);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_async_oneway(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id;
 | 
						|
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    id = gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY,
 | 
						|
        req, test_async_oneway_done, NULL, loop);
 | 
						|
    g_assert(id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * sync_oneway
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_sync_oneway(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
 | 
						|
    test_binder_br_transaction_complete(fd, THIS_THREAD);
 | 
						|
    g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * sync_reply_ok
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_sync_reply_ok_status(
 | 
						|
    int* status)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderLocalReply* reply = test_local_reply_new(ipc);
 | 
						|
    GBinderRemoteReply* tx_reply;
 | 
						|
    GBinderOutputData* data;
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const guint32 handle = 0;
 | 
						|
    const guint32 code = 1;
 | 
						|
    const char* result_in = "foo";
 | 
						|
    char* result_out;
 | 
						|
 | 
						|
    g_assert(gbinder_local_reply_append_string16(reply, result_in));
 | 
						|
    data = gbinder_local_reply_data(reply);
 | 
						|
    g_assert(data);
 | 
						|
 | 
						|
    test_binder_br_noop(fd, THIS_THREAD);
 | 
						|
    test_binder_br_transaction_complete(fd, THIS_THREAD);
 | 
						|
    test_binder_br_noop(fd, THIS_THREAD);
 | 
						|
    test_binder_br_reply(fd, THIS_THREAD, handle, code, data->bytes);
 | 
						|
 | 
						|
    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);
 | 
						|
    g_assert(!g_strcmp0(result_out, result_in));
 | 
						|
    g_free(result_out);
 | 
						|
 | 
						|
    gbinder_remote_reply_unref(tx_reply);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_local_reply_unref(reply);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_sync_reply_ok(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    int status = -1;
 | 
						|
 | 
						|
    test_sync_reply_ok_status(NULL);
 | 
						|
    test_sync_reply_ok_status(&status);
 | 
						|
    g_assert(status == GBINDER_STATUS_OK);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * sync_reply_error
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_sync_reply_error(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const guint32 handle = 0;
 | 
						|
    const guint32 code = 1;
 | 
						|
    const gint expected_status = (-EINVAL);
 | 
						|
    const gint unexpected_status = GBINDER_STATUS_FAILED;
 | 
						|
    int status = INT_MAX;
 | 
						|
 | 
						|
    test_binder_ignore_dead_object(fd);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_reply_status(fd, TX_THREAD, expected_status);
 | 
						|
 | 
						|
    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
 | 
						|
    g_assert_cmpint(status, == ,expected_status);
 | 
						|
 | 
						|
    /* GBINDER_STATUS_FAILED gets replaced with -EFAULT */
 | 
						|
    test_binder_ignore_dead_object(fd);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_reply_status(fd, TX_THREAD, unexpected_status);
 | 
						|
 | 
						|
    g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status));
 | 
						|
    g_assert_cmpint(status, == ,-EFAULT);
 | 
						|
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_ok
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
#define TEST_REQ_PARAM_STR "foo"
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_ok_destroy(
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_ok_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    char* result;
 | 
						|
 | 
						|
    GVERBOSE_("");
 | 
						|
    result = gbinder_remote_reply_read_string16(reply);
 | 
						|
    g_assert(!g_strcmp0(result, TEST_REQ_PARAM_STR));
 | 
						|
    g_free(result);
 | 
						|
    g_assert(status == GBINDER_STATUS_OK);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_ok(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderLocalReply* reply = test_local_reply_new(ipc);
 | 
						|
    GBinderOutputData* data;
 | 
						|
    const guint32 handle = 0;
 | 
						|
    const guint32 code = 1;
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id;
 | 
						|
 | 
						|
    g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR));
 | 
						|
    data = gbinder_local_reply_data(reply);
 | 
						|
    g_assert(data);
 | 
						|
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_reply(fd, TX_THREAD, handle, code, data->bytes);
 | 
						|
 | 
						|
    id = gbinder_ipc_transact(ipc, handle, code, 0, req,
 | 
						|
        test_transact_ok_done, test_transact_ok_destroy, loop);
 | 
						|
    g_assert(id);
 | 
						|
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Transaction id is not valid anymore: */
 | 
						|
    gbinder_ipc_cancel(ipc, id);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_local_reply_unref(reply);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_dead
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_dead_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("%d", status);
 | 
						|
    g_assert(!reply);
 | 
						|
    g_assert(status == GBINDER_STATUS_DEAD_OBJECT);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_dead(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id;
 | 
						|
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_dead_reply(fd, TX_THREAD);
 | 
						|
 | 
						|
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_dead_done,
 | 
						|
        NULL, loop);
 | 
						|
    g_assert(id);
 | 
						|
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Transaction id is not valid anymore: */
 | 
						|
    gbinder_ipc_cancel(ipc, id);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_failed
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_failed_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("%d", status);
 | 
						|
    g_assert(!reply);
 | 
						|
    g_assert(status == GBINDER_STATUS_FAILED);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_failed(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id;
 | 
						|
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_failed_reply(fd, TX_THREAD);
 | 
						|
 | 
						|
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_failed_done,
 | 
						|
        NULL, loop);
 | 
						|
    g_assert(id);
 | 
						|
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Transaction id is not valid anymore: */
 | 
						|
    gbinder_ipc_cancel(ipc, id);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_status
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
#define EXPECTED_STATUS (0x42424242)
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_status_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("%d", status);
 | 
						|
    g_assert(!reply);
 | 
						|
    g_assert(status == EXPECTED_STATUS);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_status(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id;
 | 
						|
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_reply_status(fd, TX_THREAD, EXPECTED_STATUS);
 | 
						|
 | 
						|
    id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_status_done,
 | 
						|
        NULL, loop);
 | 
						|
    g_assert(id);
 | 
						|
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Transaction id is not valid anymore: */
 | 
						|
    gbinder_ipc_cancel(ipc, id);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_custom
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom_done(
 | 
						|
    const GBinderIpcTx* tx)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    test_quit_later((GMainLoop*)tx->user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id = gbinder_ipc_transact_custom(ipc, NULL,
 | 
						|
        test_transact_custom_done, NULL, loop);
 | 
						|
 | 
						|
    g_assert(id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_ipc_exit();
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_custom2
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom_destroy(
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom2(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id = gbinder_ipc_transact_custom(ipc, NULL, NULL,
 | 
						|
        test_transact_custom_destroy, loop);
 | 
						|
 | 
						|
    g_assert(id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_ipc_exit();
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_custom3
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom3_exec(
 | 
						|
    const GBinderIpcTx* tx)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    gbinder_ipc_unref(tx->ipc);
 | 
						|
    test_quit_later((GMainLoop*)tx->user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_custom3(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */
 | 
						|
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_custom3_exec,
 | 
						|
        NULL, NULL, loop);
 | 
						|
 | 
						|
    g_assert(id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Reference to GBinderIpc is released by test_transact_custom3_exec */
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_cancel
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel_destroy(
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel_exec(
 | 
						|
    const GBinderIpcTx* tx)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel_done(
 | 
						|
    const GBinderIpcTx* tx)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    g_assert(tx->cancelled);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel_exec,
 | 
						|
        test_transact_cancel_done, test_transact_cancel_destroy, loop);
 | 
						|
 | 
						|
    g_assert(id);
 | 
						|
    gbinder_ipc_cancel(ipc, id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_cancel2
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
gboolean
 | 
						|
test_transact_cancel2_cancel(
 | 
						|
    gpointer data)
 | 
						|
{
 | 
						|
    const GBinderIpcTx* tx = data;
 | 
						|
 | 
						|
    GVERBOSE_("");
 | 
						|
    gbinder_ipc_cancel(tx->ipc, tx->id);
 | 
						|
    return G_SOURCE_REMOVE;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel2_exec(
 | 
						|
    const GBinderIpcTx* tx)
 | 
						|
{
 | 
						|
    GVERBOSE_("");
 | 
						|
    g_assert(!tx->cancelled);
 | 
						|
    g_main_context_invoke(NULL, test_transact_cancel2_cancel, (void*)tx);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_cancel2(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */
 | 
						|
    gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel2_exec,
 | 
						|
        test_transact_cancel_done, test_transact_cancel_destroy, loop);
 | 
						|
 | 
						|
    g_assert(id);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_2way
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_transact_2way_incoming_proc(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    GBinderRemoteRequest* req,
 | 
						|
    guint code,
 | 
						|
    guint flags,
 | 
						|
    int* status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    int* incoming_call = user_data;
 | 
						|
 | 
						|
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
						|
    g_assert_cmpuint(flags, == ,0);
 | 
						|
    g_assert_cmpint(gbinder_remote_request_sender_pid(req), == ,getpid());
 | 
						|
    g_assert_cmpint(gbinder_remote_request_sender_euid(req), == ,geteuid());
 | 
						|
    g_assert_cmpstr(gbinder_remote_request_interface(req), == ,"test");
 | 
						|
    g_assert_cmpstr(gbinder_remote_request_read_string8(req), == ,"message");
 | 
						|
    g_assert_cmpuint(code, == ,2);
 | 
						|
    g_assert_cmpint(*incoming_call, == ,0);
 | 
						|
    (*incoming_call)++;
 | 
						|
 | 
						|
    *status = GBINDER_STATUS_OK;
 | 
						|
    return gbinder_local_object_new_reply(obj);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_2way_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
						|
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
						|
    const char* const ifaces[] = { "test", NULL };
 | 
						|
    const guint32 handle = 0;
 | 
						|
    const guint32 code = 1;
 | 
						|
    int incoming_call = 0;
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, ifaces, test_transact_2way_incoming_proc, &incoming_call);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderLocalRequest* incoming_req = test_local_request_new(ipc);
 | 
						|
    GBinderLocalReply* reply = test_local_reply_new(ipc);
 | 
						|
    GBinderWriter writer;
 | 
						|
 | 
						|
    /* Prepare reply */
 | 
						|
    g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR));
 | 
						|
 | 
						|
    /* Prepare incoming request */
 | 
						|
    gbinder_local_request_init_writer(req, &writer);
 | 
						|
    prot->write_rpc_header(&writer, "test");
 | 
						|
    gbinder_writer_append_string8(&writer, "message");
 | 
						|
 | 
						|
    test_binder_ignore_dead_object(fd);
 | 
						|
    test_binder_br_transaction(fd, TX_THREAD, obj, 2,
 | 
						|
        gbinder_local_request_data(req)->bytes);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    test_binder_br_noop(fd, TX_THREAD);
 | 
						|
    test_binder_br_reply(fd, TX_THREAD, handle, code,
 | 
						|
        gbinder_local_reply_data(reply)->bytes);
 | 
						|
 | 
						|
    /* NB. Reusing test_transact_ok_done and test_transact_ok_destroy */
 | 
						|
    g_assert(gbinder_ipc_transact(ipc, handle, code, 0, req,
 | 
						|
        test_transact_ok_done, test_transact_ok_destroy, loop));
 | 
						|
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
						|
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
						|
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_local_request_unref(incoming_req);
 | 
						|
    gbinder_local_reply_unref(reply);
 | 
						|
    g_idle_add(test_unref_ipc, ipc);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_2way(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_2way_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_unhandled
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_unhandled_done(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    g_assert(!reply);
 | 
						|
    g_assert_cmpint(status, == ,GBINDER_STATUS_DEAD_OBJECT);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_unhandled_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderDriver* driver = ipc->driver;
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver);
 | 
						|
 | 
						|
    g_assert(gbinder_ipc_transact(ipc, 1 /* Non-existent object */,
 | 
						|
        gbinder_driver_protocol(driver)->ping_tx, 0, req,
 | 
						|
        test_transact_unhandled_done, NULL, loop));
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_unhandled(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_unhandled_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_incoming
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_transact_incoming_proc(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    GBinderRemoteRequest* req,
 | 
						|
    guint code,
 | 
						|
    guint flags,
 | 
						|
    int* status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
						|
    g_assert(!flags);
 | 
						|
    g_assert(gbinder_remote_request_sender_pid(req) == getpid());
 | 
						|
    g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
						|
    g_assert(code == 1);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
 | 
						|
    *status = GBINDER_STATUS_OK;
 | 
						|
    return gbinder_local_object_new_reply(obj);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_incoming_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
						|
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
						|
    const char* const ifaces[] = { "test", NULL };
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, ifaces, test_transact_incoming_proc, loop);
 | 
						|
    GBinderLocalRequest* ping = test_local_request_new(ipc);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderWriter writer;
 | 
						|
 | 
						|
    gbinder_local_request_init_writer(ping, &writer);
 | 
						|
    prot->write_ping(&writer);
 | 
						|
 | 
						|
    gbinder_local_request_init_writer(req, &writer);
 | 
						|
    prot->write_rpc_header(&writer, "test");
 | 
						|
    gbinder_writer_append_string8(&writer, "message");
 | 
						|
 | 
						|
    test_binder_br_transaction(fd, LOOPER_THREAD, obj, prot->ping_tx,
 | 
						|
        gbinder_local_request_data(ping)->bytes);
 | 
						|
    test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */
 | 
						|
    test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1,
 | 
						|
        gbinder_local_request_data(req)->bytes);
 | 
						|
    test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
						|
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
						|
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
    gbinder_local_request_unref(ping);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    g_idle_add(test_unref_ipc, ipc);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_incoming(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_incoming_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_status_reply
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_transact_status_reply_proc(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    GBinderRemoteRequest* req,
 | 
						|
    guint code,
 | 
						|
    guint flags,
 | 
						|
    int* status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
						|
    g_assert(!flags);
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
						|
    g_assert(code == 1);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
 | 
						|
    *status = EXPECTED_STATUS;
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_status_reply_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
						|
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
						|
    const char* const ifaces[] = { "test", NULL };
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, ifaces, test_transact_status_reply_proc, loop);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderOutputData* data;
 | 
						|
    GBinderWriter writer;
 | 
						|
 | 
						|
    gbinder_local_request_init_writer(req, &writer);
 | 
						|
    prot->write_rpc_header(&writer, "test");
 | 
						|
    gbinder_writer_append_string8(&writer, "message");
 | 
						|
    data = gbinder_local_request_data(req);
 | 
						|
 | 
						|
    test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes);
 | 
						|
    test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
						|
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
						|
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    g_idle_add(test_unref_ipc, ipc);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_status_reply(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_status_reply_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_async
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
typedef struct test_transact_async_req {
 | 
						|
    GBinderLocalObject* obj;
 | 
						|
    GBinderRemoteRequest* req;
 | 
						|
    GMainLoop* loop;
 | 
						|
} TestTransactAsyncReq;
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_async_done(
 | 
						|
    gpointer data)
 | 
						|
{
 | 
						|
    TestTransactAsyncReq* test = data;
 | 
						|
 | 
						|
    gbinder_local_object_unref(test->obj);
 | 
						|
    gbinder_remote_request_unref(test->req);
 | 
						|
    test_quit_later(test->loop);
 | 
						|
    g_free(test);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
gboolean
 | 
						|
test_transact_async_reply(
 | 
						|
    gpointer data)
 | 
						|
{
 | 
						|
    TestTransactAsyncReq* test = data;
 | 
						|
    GBinderLocalReply* reply = gbinder_local_object_new_reply(test->obj);
 | 
						|
 | 
						|
    gbinder_remote_request_complete(test->req, reply, 0);
 | 
						|
    gbinder_local_reply_unref(reply);
 | 
						|
    return G_SOURCE_REMOVE;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_transact_async_proc(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    GBinderRemoteRequest* req,
 | 
						|
    guint code,
 | 
						|
    guint flags,
 | 
						|
    int* status,
 | 
						|
    void* loop)
 | 
						|
{
 | 
						|
    TestTransactAsyncReq* test = g_new(TestTransactAsyncReq, 1);
 | 
						|
 | 
						|
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
						|
    g_assert(!flags);
 | 
						|
    g_assert(gbinder_remote_request_sender_pid(req) == getpid());
 | 
						|
    g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
						|
    g_assert(code == 1);
 | 
						|
 | 
						|
    test->obj = gbinder_local_object_ref(obj);
 | 
						|
    test->req = gbinder_remote_request_ref(req);
 | 
						|
    test->loop = (GMainLoop*)loop;
 | 
						|
 | 
						|
    gbinder_remote_request_block(req);
 | 
						|
    gbinder_remote_request_block(req); /* wrong state; has no effect */
 | 
						|
 | 
						|
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_transact_async_reply, test,
 | 
						|
        test_transact_async_done);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_async_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
						|
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
						|
    const char* const ifaces[] = { "test", NULL };
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, ifaces, test_transact_async_proc, loop);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderOutputData* data;
 | 
						|
    GBinderWriter writer;
 | 
						|
 | 
						|
    gbinder_local_request_init_writer(req, &writer);
 | 
						|
    prot->write_rpc_header(&writer, "test");
 | 
						|
    gbinder_writer_append_string8(&writer, "message");
 | 
						|
    data = gbinder_local_request_data(req);
 | 
						|
 | 
						|
    test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes);
 | 
						|
    test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
						|
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
						|
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    g_idle_add(test_unref_ipc, ipc);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_async(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_async_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * transact_async_sync
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
GBinderLocalReply*
 | 
						|
test_transact_async_sync_proc(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    GBinderRemoteRequest* req,
 | 
						|
    guint code,
 | 
						|
    guint flags,
 | 
						|
    int* status,
 | 
						|
    void* loop)
 | 
						|
{
 | 
						|
    GBinderLocalReply* reply = gbinder_local_object_new_reply(obj);
 | 
						|
 | 
						|
    GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code);
 | 
						|
    g_assert(!flags);
 | 
						|
    g_assert(gbinder_remote_request_sender_pid(req) == getpid());
 | 
						|
    g_assert(gbinder_remote_request_sender_euid(req) == geteuid());
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test"));
 | 
						|
    g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message"));
 | 
						|
    g_assert(code == 1);
 | 
						|
 | 
						|
    /* Block and immediately complete the call */
 | 
						|
    gbinder_remote_request_block(req);
 | 
						|
    gbinder_remote_request_complete(req, reply, 0);
 | 
						|
    gbinder_remote_request_complete(req, reply, 0); /* This one is ignored */
 | 
						|
    gbinder_local_reply_unref(reply);
 | 
						|
 | 
						|
    test_quit_later((GMainLoop*)loop);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_async_sync_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    const int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    const char* dev = gbinder_driver_dev(ipc->driver);
 | 
						|
    const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev);
 | 
						|
    const char* const ifaces[] = { "test", NULL };
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, ifaces, test_transact_async_sync_proc, loop);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GBinderOutputData* data;
 | 
						|
    GBinderWriter writer;
 | 
						|
 | 
						|
    gbinder_local_request_init_writer(req, &writer);
 | 
						|
    prot->write_rpc_header(&writer, "test");
 | 
						|
    gbinder_writer_append_string8(&writer, "message");
 | 
						|
    data = gbinder_local_request_data(req);
 | 
						|
 | 
						|
    test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes);
 | 
						|
    test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    /* Now we need to wait until GBinderIpc is destroyed */
 | 
						|
    GDEBUG("waiting for GBinderIpc to get destroyed");
 | 
						|
    g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    g_idle_add(test_unref_ipc, ipc);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_transact_async_sync(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_transact_async_sync_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * drop_remote_refs
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_drop_remote_refs_cb(
 | 
						|
    GBinderLocalObject* obj,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    GVERBOSE_("%d", obj->strong_refs);
 | 
						|
    g_assert(obj->strong_refs == 1);
 | 
						|
    test_quit_later((GMainLoop*)user_data);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_drop_remote_refs_run(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalObject* obj = gbinder_local_object_new
 | 
						|
        (ipc, NULL, NULL, NULL);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
    gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj,
 | 
						|
        test_drop_remote_refs_cb, loop);
 | 
						|
 | 
						|
    test_binder_br_acquire(fd, ANY_THREAD, obj);
 | 
						|
    test_run(&test_opt, loop);
 | 
						|
 | 
						|
    g_assert(obj->strong_refs == 1);
 | 
						|
    gbinder_local_object_remove_handler(obj, id);
 | 
						|
    gbinder_local_object_unref(obj);
 | 
						|
 | 
						|
    /* gbinder_ipc_exit will drop the remote reference */
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    gbinder_ipc_exit();
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_drop_remote_refs(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    test_run_in_context(&test_opt, test_drop_remote_refs_run);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * cancel_on_exit
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_cancel_on_exit_not_reached(
 | 
						|
    GBinderIpc* ipc,
 | 
						|
    GBinderRemoteReply* reply,
 | 
						|
    int status,
 | 
						|
    void* user_data)
 | 
						|
{
 | 
						|
    g_assert_not_reached();
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
void
 | 
						|
test_cancel_on_exit(
 | 
						|
    void)
 | 
						|
{
 | 
						|
    GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
 | 
						|
    GBinderLocalRequest* req = test_local_request_new(ipc);
 | 
						|
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
 | 
						|
    int fd = gbinder_driver_fd(ipc->driver);
 | 
						|
 | 
						|
    /* This transaction will be cancelled by gbinder_ipc_exit */
 | 
						|
    test_binder_br_transaction_complete(fd, TX_THREAD);
 | 
						|
    gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY,
 | 
						|
        req, test_cancel_on_exit_not_reached, NULL, NULL);
 | 
						|
 | 
						|
    gbinder_local_request_unref(req);
 | 
						|
    gbinder_ipc_unref(ipc);
 | 
						|
    gbinder_ipc_exit();
 | 
						|
    test_binder_exit_wait(&test_opt, loop);
 | 
						|
    g_main_loop_unref(loop);
 | 
						|
}
 | 
						|
 | 
						|
/*==========================================================================*
 | 
						|
 * Common
 | 
						|
 *==========================================================================*/
 | 
						|
 | 
						|
#define TEST_PREFIX "/ipc/"
 | 
						|
#define TEST_(t) TEST_PREFIX t
 | 
						|
 | 
						|
int main(int argc, char* argv[])
 | 
						|
{
 | 
						|
    TestConfig test_config;
 | 
						|
    int result;
 | 
						|
 | 
						|
    G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
 | 
						|
    g_type_init();
 | 
						|
    G_GNUC_END_IGNORE_DEPRECATIONS;
 | 
						|
    g_test_init(&argc, &argv, NULL);
 | 
						|
    g_test_add_func(TEST_("null"), test_null);
 | 
						|
    g_test_add_func(TEST_("basic"), test_basic);
 | 
						|
    g_test_add_func(TEST_("protocol"), test_protocol);
 | 
						|
    g_test_add_func(TEST_("async_oneway"), test_async_oneway);
 | 
						|
    g_test_add_func(TEST_("sync_oneway"), test_sync_oneway);
 | 
						|
    g_test_add_func(TEST_("sync_reply_ok"), test_sync_reply_ok);
 | 
						|
    g_test_add_func(TEST_("sync_reply_error"), test_sync_reply_error);
 | 
						|
    g_test_add_func(TEST_("transact_ok"), test_transact_ok);
 | 
						|
    g_test_add_func(TEST_("transact_dead"), test_transact_dead);
 | 
						|
    g_test_add_func(TEST_("transact_failed"), test_transact_failed);
 | 
						|
    g_test_add_func(TEST_("transact_status"), test_transact_status);
 | 
						|
    g_test_add_func(TEST_("transact_custom"), test_transact_custom);
 | 
						|
    g_test_add_func(TEST_("transact_custom2"), test_transact_custom2);
 | 
						|
    g_test_add_func(TEST_("transact_custom3"), test_transact_custom3);
 | 
						|
    g_test_add_func(TEST_("transact_cancel"), test_transact_cancel);
 | 
						|
    g_test_add_func(TEST_("transact_cancel2"), test_transact_cancel2);
 | 
						|
    g_test_add_func(TEST_("transact_2way"), test_transact_2way);
 | 
						|
    g_test_add_func(TEST_("transact_incoming"), test_transact_incoming);
 | 
						|
    g_test_add_func(TEST_("transact_unhandled"), test_transact_unhandled);
 | 
						|
    g_test_add_func(TEST_("transact_status_reply"), test_transact_status_reply);
 | 
						|
    g_test_add_func(TEST_("transact_async"), test_transact_async);
 | 
						|
    g_test_add_func(TEST_("transact_async_sync"), test_transact_async_sync);
 | 
						|
    g_test_add_func(TEST_("drop_remote_refs"), test_drop_remote_refs);
 | 
						|
    g_test_add_func(TEST_("cancel_on_exit"), test_cancel_on_exit);
 | 
						|
    test_init(&test_opt, argc, argv);
 | 
						|
    test_config_init(&test_config, TMP_DIR_TEMPLATE);
 | 
						|
    result = g_test_run();
 | 
						|
    test_config_cleanup(&test_config);
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Local Variables:
 | 
						|
 * mode: C
 | 
						|
 * c-basic-offset: 4
 | 
						|
 * indent-tabs-mode: nil
 | 
						|
 * End:
 | 
						|
 */
 |