[test] Add binder-call. JB#55084

This commit is contained in:
Frajo Haider
2021-08-13 19:18:23 +00:00
parent ca665ad3d1
commit 3a4ae9a716
7 changed files with 1516 additions and 0 deletions

View File

@@ -11,6 +11,8 @@ Source: %{name}-%{version}.tar.bz2
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version}
BuildRequires: bison
BuildRequires: flex
Requires: libglibutil >= %{libglibutil_version}
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
@@ -34,6 +36,7 @@ make %{_smp_mflags} LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
make -C test/binder-bridge KEEP_SYMBOLS=1 release
make -C test/binder-list KEEP_SYMBOLS=1 release
make -C test/binder-ping KEEP_SYMBOLS=1 release
make -C test/binder-call KEEP_SYMBOLS=1 release
%install
rm -rf %{buildroot}
@@ -41,6 +44,7 @@ make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev
make -C test/binder-bridge DESTDIR=%{buildroot} install
make -C test/binder-list DESTDIR=%{buildroot} install
make -C test/binder-ping DESTDIR=%{buildroot} install
make -C test/binder-call DESTDIR=%{buildroot} install
%check
make -C unit test
@@ -73,3 +77,4 @@ Binder command line utilities
%{_bindir}/binder-bridge
%{_bindir}/binder-list
%{_bindir}/binder-ping
%{_bindir}/binder-call

View File

@@ -8,4 +8,5 @@ all:
@$(MAKE) -C binder-list $*
@$(MAKE) -C binder-ping $*
@$(MAKE) -C binder-service $*
@$(MAKE) -C binder-call $*
@$(MAKE) -C rild-card-status $*

178
test/binder-call/Makefile Normal file
View File

@@ -0,0 +1,178 @@
# -*- Mode: makefile-gmake -*-
.PHONY: all debug release clean cleaner
.PHONY: libgbinder-release libgbinder-debug
#
# Required packages
#
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
#
# Default target
#
all: debug release
#
# Executable
#
EXE = binder-call
#
# Sources
#
SRC = $(EXE).c
GEN_SRC = \
cmdline.tab.c \
lex.cmdline.c
#
# Directories
#
SRC_DIR = .
BUILD_DIR = build
GEN_DIR = $(BUILD_DIR)
LIB_DIR = ../..
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
#
# Tools and flags
#
CC ?= $(CROSS_COMPILE)gcc
LD = $(CC)
WARNINGS = -Wall
INCLUDES = -I$(LIB_DIR)/include -I$(GEN_DIR) -I$(SRC_DIR)
BASE_FLAGS = -fPIC
CFLAGS = $(BASE_FLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \
$(shell pkg-config --cflags $(PKGS))
LDFLAGS = $(BASE_FLAGS) $(shell pkg-config --libs $(PKGS))
QUIET_MAKE = make --no-print-directory
DEBUG_FLAGS = -g
RELEASE_FLAGS =
ifndef KEEP_SYMBOLS
KEEP_SYMBOLS = 0
endif
ifneq ($(KEEP_SYMBOLS),0)
RELEASE_FLAGS += -g
SUBMAKE_OPTS += KEEP_SYMBOLS=1
endif
DEBUG_LDFLAGS = $(LDFLAGS) $(DEBUG_FLAGS)
RELEASE_LDFLAGS = $(LDFLAGS) $(RELEASE_FLAGS)
DEBUG_CFLAGS = $(CFLAGS) $(DEBUG_FLAGS) -DDEBUG
RELEASE_CFLAGS = $(CFLAGS) $(RELEASE_FLAGS) -O2
#
# Files
#
DEBUG_OBJS = $(GEN_SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) \
$(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
RELEASE_OBJS = $(GEN_SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) \
$(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
DEBUG_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_so)
RELEASE_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_so)
DEBUG_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_link)
RELEASE_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_link)
DEBUG_SO = $(LIB_DIR)/$(DEBUG_SO_FILE)
RELEASE_SO = $(LIB_DIR)/$(RELEASE_SO_FILE)
GEN_FILES = $(GEN_SRC:%=$(GEN_DIR)/%)
.PRECIOUS: $(GEN_FILES)
#
# Dependencies
#
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d)
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(DEPS)),)
-include $(DEPS)
endif
endif
$(GEN_FILES): | $(GEN_DIR)
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
#
# Rules
#
DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE)
RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE)
debug: libgbinder-debug $(DEBUG_EXE)
release: libgbinder-release $(RELEASE_EXE)
clean:
rm -f *~
rm -fr $(BUILD_DIR)
cleaner: clean
@make -C $(LIB_DIR) clean
$(DEBUG_BUILD_DIR):
mkdir -p $@
$(RELEASE_BUILD_DIR):
mkdir -p $@
$(GEN_DIR):
mkdir -p $@
$(GEN_DIR)/%.tab.c : $(SRC_DIR)/%.y
bison -pcmdline -bcmdline -d -o $@ $<
$(GEN_DIR)/lex.%.c : $(SRC_DIR)/%.l
flex -o $@ $<
$(DEBUG_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
$(DEBUG_EXE): $(DEBUG_SO) $(DEBUG_BUILD_DIR) $(DEBUG_OBJS)
$(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) $< -o $@
$(RELEASE_EXE): $(RELEASE_SO) $(RELEASE_BUILD_DIR) $(RELEASE_OBJS)
$(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) $< -o $@
ifeq ($(KEEP_SYMBOLS),0)
strip $@
endif
libgbinder-debug:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(DEBUG_SO_FILE) $(DEBUG_LINK_FILE)
libgbinder-release:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)
#
# Install
#
INSTALL = install
INSTALL_BIN_DIR = $(DESTDIR)/usr/bin
install: release $(INSTALL_BIN_DIR)
$(INSTALL) -m 755 $(RELEASE_EXE) $(INSTALL_BIN_DIR)
$(INSTALL_BIN_DIR):
$(INSTALL) -d $@

View File

@@ -0,0 +1,768 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2021 Franz-Josef Haider <franz.haider@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <gbinder.h>
#include <gutil_log.h>
#include <unistd.h>
#include <binder-call.h>
#include <stdlib.h>
struct transaction_and_reply* ast;
#define RET_OK (0)
#define RET_NOTFOUND (1)
#define RET_INVARG (2)
#define RET_ERR (3)
#define DEFAULT_DEVICE GBINDER_DEFAULT_BINDER
#define GBINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4)
#define GBINDER_INTERFACE_TRANSACTION GBINDER_TRANSACTION('N','T','F')
static const char pname[] = "binder-call";
enum transaction_pass {
COMPUTE_SIZES = 0,
FILL_BUFFERS,
BUILD_TRANSACTION
};
enum reply_pass {
PRINT_REPLY = 0,
COMPUTE_SIZES_REPLY
};
static
int
go_through_transaction_ast(
App* app,
GList* node_list,
int parent_idx,
void* buf,
enum transaction_pass cur_pass,
int cont_offset)
{
GList* l;
int offset = cont_offset;
for (l = node_list; l; l = l->next) {
struct value_info* v = l->data;
switch(v->type) {
case INT8_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int8");
if (parent_idx == -1) {
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((unsigned char*)(((char*)buf)+offset)) = *((unsigned char*)v->value);
}
offset++;
break;
case INT32_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int32");
if (parent_idx == -1) {
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((int*)(((char*)buf)+offset)) = *((int*)v->value);
}
offset += sizeof(gint32);
break;
case INT64_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int64");
if (parent_idx == -1) {
gbinder_writer_append_int64(&app->writer, *((gint64*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((gint64*)(((char*)buf)+offset)) = *((gint64*)v->value);
}
offset += sizeof(gint64);
break;
case FLOAT_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("float");
if (parent_idx == -1) {
gbinder_writer_append_float(&app->writer, *((float*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((float*)(((char*)buf)+offset)) = *((float*)v->value);
}
offset += sizeof(float);
break;
case DOUBLE_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("double");
if (parent_idx == -1) {
gbinder_writer_append_double(&app->writer, *((double*)v->value));
} else if (cur_pass == FILL_BUFFERS) {
*((double*)(((char*)buf)+offset)) = *((double*)v->value);
}
offset += sizeof(double);
break;
case STRING8_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string8");
gbinder_writer_append_string8(&app->writer, v->value);
// offset not incremented since it only makes sense for hidl
break;
case STRING16_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string16");
gbinder_writer_append_string16(&app->writer, v->value);
// offset not incremented since it only makes sense for hidl
break;
case HSTRING_TYPE:
if (cur_pass == BUILD_TRANSACTION) GDEBUG("hstring");
if (parent_idx == -1) {
gbinder_writer_append_hidl_string(&app->writer, v->value);
} else {
GBinderHidlString* hidl_str = (GBinderHidlString*)(((char*)buf)+offset);
if (cur_pass == FILL_BUFFERS) {
hidl_str->data.str = v->value;
hidl_str->len = strlen(v->value);
hidl_str->owns_buffer = TRUE;
}
if (cur_pass == BUILD_TRANSACTION) {
GBinderParent p;
p.index = parent_idx;
p.offset = offset;
gbinder_writer_append_buffer_object_with_parent(&app->writer, hidl_str->data.str, hidl_str->len+1, &p);
}
}
offset += sizeof(GBinderHidlString);
break;
case STRUCT_TYPE: {
if (cur_pass == BUILD_TRANSACTION) GDEBUG("struct");
if (!app->opt->aidl) {
if (parent_idx == -1) {
int s = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
void *new_buf = gbinder_writer_malloc(&app->writer, s);
int new_parent_idx;
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
new_parent_idx = gbinder_writer_append_buffer_object(&app->writer, new_buf, s);
// if parent_idx == -1 there is no need to update the offset,
// since we are processing the argument list and are not
// inside an argument.
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
} else {
if (cur_pass == FILL_BUFFERS) {
// fill struct mode.
offset += go_through_transaction_ast(app, v->value, 0, ((char*)buf)+offset, cur_pass, 0);
} else if (cur_pass == BUILD_TRANSACTION) {
int s = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
go_through_transaction_ast(app, v->value, 0, buf, FILL_BUFFERS, offset);
go_through_transaction_ast(app, v->value, /*new_*/parent_idx, buf, BUILD_TRANSACTION, offset);
offset += s;
} else if (cur_pass == COMPUTE_SIZES) {
offset += go_through_transaction_ast(app, v->value, 0, NULL, cur_pass, 0);
}
}
} else {
go_through_transaction_ast(app, v->value, -1, NULL, cur_pass, 0);
}
if (cur_pass == BUILD_TRANSACTION) GDEBUG("structend");
break;
}
case VECTOR_TYPE: {
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vector");
if (!app->opt->aidl) {
if (parent_idx == -1) {
GBinderHidlVec* vec;
int vs = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
int es = go_through_transaction_ast(app, g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
void* new_buf = gbinder_writer_malloc(&app->writer, vs);
int new_parent_idx;
GBinderParent vec_parent;
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
vec = gbinder_writer_new0(&app->writer, GBinderHidlVec);
vec->data.ptr = new_buf;
vec->count = vs / es;
if (vec->count != g_list_length(v->value)) {
GERR("SEMANTIC ERROR VECTOR");
abort();
}
vec_parent.index = gbinder_writer_append_buffer_object(&app->writer, vec, sizeof(*vec));
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
new_parent_idx = gbinder_writer_append_buffer_object_with_parent(&app->writer, new_buf, vs, &vec_parent);
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
} else {
if (cur_pass == FILL_BUFFERS) {
// fill struct mode.
int sl = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
int es = go_through_transaction_ast(app, g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
void* new_buf = gbinder_writer_malloc(&app->writer, sl);
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
vec->data.ptr = new_buf;
vec->count = sl / es;
} else if (cur_pass == BUILD_TRANSACTION) {
void* new_buf;
int new_parent_idx;
int sl = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
GBinderParent p;
new_buf = (void*)vec->data.ptr;
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
if (vec->count != g_list_length(v->value)) {
GERR("SEMANTIC ERROR VECTOR");
abort();
}
p.index = parent_idx;
p.offset = offset;
new_parent_idx = gbinder_writer_append_buffer_object_with_parent(&app->writer, new_buf, sl, &p);
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
}
offset += sizeof(GBinderHidlVec);
}
} else {
int vl = g_list_length(v->value);
gbinder_writer_append_int32(&app->writer, vl);
go_through_transaction_ast(app, v->value, -1, NULL, cur_pass, 0);
}
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vectorend");
break;
}
default:
GERR("unknown type: %d\n", v->type);
break;
};
}
return offset;
}
static
int
go_through_reply_ast(
App* app,
GList* node_list,
struct type_info* tt,
const void* buf,
enum reply_pass cur_pass)
{
GList* l;
int offset = 0;
for (l = node_list; l || tt; l = l->next) {
struct type_info* t = node_list ? l->data : tt;
switch(t->type) {
case INT8_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("int8");
if (cur_pass != COMPUTE_SIZES_REPLY) {
int val;
if (!buf) {
gbinder_reader_read_int32(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((unsigned char*)(((char*)buf)+offset));
}
printf("%d:8 ", val);
}
offset += 1;
break;
}
case INT32_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("int32");
if (cur_pass != COMPUTE_SIZES_REPLY) {
gint32 val;
if (!buf) {
gbinder_reader_read_int32(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((gint32*)(((char*)buf)+offset));
}
printf("%d ", val);
}
offset += sizeof(gint32);
break;
}
case INT64_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("int64");
if (cur_pass != COMPUTE_SIZES_REPLY) {
gint64 val;
if (!buf) {
gbinder_reader_read_int64(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((gint64*)(((char*)buf)+offset));
}
printf("%ldL ", val);
}
offset += sizeof(gint64);
break;
}
case FLOAT_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("float");
if (cur_pass != COMPUTE_SIZES_REPLY) {
float val;
if (!buf) {
gbinder_reader_read_float(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((float*)(((char*)buf)+offset));
}
printf("%f ", val);
}
offset += sizeof(float);
break;
}
case DOUBLE_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("double");
if (cur_pass != COMPUTE_SIZES_REPLY) {
double val;
if (!buf) {
gbinder_reader_read_double(&app->reader, &val);
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
val = *((double*)(((char*)buf)+offset));
}
printf("%lfL ", val);
}
offset += sizeof(double);
break;
}
case STRING8_TYPE: {
const char* val = gbinder_reader_read_string8(&app->reader);
if (cur_pass == PRINT_REPLY) GDEBUG("string8");
printf("\"%s\" ", val);
// offset not incremented since it only makes sense for hidl
break;
}
case STRING16_TYPE: {
char* val = gbinder_reader_read_string16(&app->reader);
if (cur_pass == PRINT_REPLY) GDEBUG("string16");
printf("\"%s\"U ", val);
g_free(val);
// offset not incremented since it only makes sense for hidl
break;
}
case HSTRING_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("hstring");
if (cur_pass != COMPUTE_SIZES_REPLY) {
char* val = NULL;
if (!buf) {
val = gbinder_reader_read_hidl_string(&app->reader);
} else {
GBinderHidlString* hidl_str = (GBinderHidlString*)(((char*)buf)+offset);
val = strdup(hidl_str->data.str);
}
printf("\"%s\"H ", val);
g_free(val);
}
offset += sizeof(GBinderHidlString);
break;
}
case STRUCT_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("struct");
if (!app->opt->aidl) {
if (cur_pass == COMPUTE_SIZES_REPLY) {
offset += go_through_reply_ast(app, t->data, NULL, NULL, COMPUTE_SIZES_REPLY);
} else {
printf("{ ");
if (!buf) {
int sl = go_through_reply_ast(app, t->data, NULL, NULL, COMPUTE_SIZES_REPLY);
const void* new_buf = gbinder_reader_read_hidl_struct1(&app->reader, sl);
offset += go_through_reply_ast(app, t->data, NULL, new_buf, PRINT_REPLY);
} else {
const void* new_buf = ((char*)buf)+offset;
offset += go_through_reply_ast(app, t->data, NULL, new_buf, PRINT_REPLY);
}
printf("} ");
}
} else {
go_through_reply_ast(app, t->data, NULL, NULL, cur_pass);
}
if (cur_pass == PRINT_REPLY) GDEBUG("structend");
break;
}
case VECTOR_TYPE: {
if (cur_pass == PRINT_REPLY) GDEBUG("vector");
if (!app->opt->aidl) {
if (cur_pass != COMPUTE_SIZES_REPLY) {
if (!buf) {
guint i;
gsize count, elemsize;
const void* new_buf = gbinder_reader_read_hidl_vec(&app->reader, &count, &elemsize);
printf("[ ");
for (i = 0; i < count; i++) {
// TODO: validate elemsize somehow?
go_through_reply_ast(app, NULL, t->data, new_buf + elemsize*i, cur_pass);
}
printf("] ");
} else {
guint i;
gsize count;
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
int off;
count = vec->count;
printf("[ ");
off = 0;
for (i = 0; i < count; i++) {
off += go_through_reply_ast(app, NULL, t->data, vec->data.ptr + off, cur_pass);
}
printf("] ");
}
}
offset += sizeof(GBinderHidlVec);
} else {
guint i;
gint32 vl;
gbinder_reader_read_int32(&app->reader, &vl);
printf("[ ");
for (i = 0; i < vl; i++) {
go_through_reply_ast(app, NULL, t->data, NULL, cur_pass);
}
printf("] ");
}
if (cur_pass == PRINT_REPLY) GDEBUG("vectorend");
break;
}
default:
GERR("unknown type: %d\n", t->type);
break;
};
if (tt) break;
}
return offset;
}
static
void
go_through_ast(
App* app,
struct transaction_and_reply* ast,
gboolean transaction)
{
if (ast) {
if (transaction && ast->tree_transaction) {
go_through_transaction_ast(app, ast->tree_transaction, -1, NULL, BUILD_TRANSACTION, 0);
} else if (!transaction && ast->tree_reply) {
GDEBUG("REPLY:");
go_through_reply_ast(app, ast->tree_reply, NULL, NULL, PRINT_REPLY);
printf("\n");
}
}
}
static
void
free_ast_transaction_tree(
gpointer data)
{
struct value_info* v = data;
if (v->type == STRUCT_TYPE || v->type == VECTOR_TYPE) {
g_list_free_full(v->value, free_ast_transaction_tree);
} else {
g_free(v->value);
}
g_free(v);
}
static
void
free_ast_reply_tree(
gpointer data)
{
struct type_info* t = data;
if (t->type == VECTOR_TYPE) {
free_ast_reply_tree(t->data);
} else if (t->type == STRUCT_TYPE) {
g_list_free_full(t->data, free_ast_reply_tree);
}
g_free(t);
}
static
void
free_ast(
struct transaction_and_reply* ast)
{
if (ast) {
g_list_free_full(ast->tree_transaction, free_ast_transaction_tree);
g_list_free_full(ast->tree_reply, free_ast_reply_tree);
g_free(ast);
}
}
static
void
app_run(
App* app)
{
const AppOptions* opt = app->opt;
char* iface = opt->iface ? g_strdup(opt->iface) : NULL;
int status = 0;
int rargc = 1;
char* service = opt->argv[rargc++];
int code = atoi(opt->argv[rargc++]);
GBinderClient* client;
GBinderLocalRequest* req;
GBinderRemoteReply* reply;
GBinderRemoteObject* obj;
if (!code) {
GERR("Transaction code must be > GBINDER_FIRST_CALL_TRANSACTION(=1).");
return;
}
obj = gbinder_servicemanager_get_service_sync(app->sm,
service, &status);
if (!obj) {
GERR("No such service: %s", service);
g_free(iface);
return;
}
if (strstr(service, "/") != NULL) {
iface = g_strndup(service, strchr(service, '/') - service);
} else {
GBinderReader reader;
client = gbinder_client_new(obj, NULL);
req = gbinder_client_new_request(client);
reply = gbinder_client_transact_sync_reply(client, GBINDER_INTERFACE_TRANSACTION, req, &status);
gbinder_remote_reply_init_reader(reply, &reader);
iface = gbinder_reader_read_string16(&reader);
gbinder_local_request_unref(req);
gbinder_remote_reply_unref(reply);
gbinder_client_unref(client);
}
if (!iface) {
GERR("Failed to get interface");
return;
}
GDEBUG("Got iface: %s", iface);
client = gbinder_client_new(obj, iface);
g_free(iface);
req = gbinder_client_new_request(client);
app->rargc = rargc;
app->code = code;
cmdline_parse(app);
gbinder_local_request_init_writer(req, &app->writer);
go_through_ast(app, ast, TRUE);
if (opt->oneway) {
gbinder_client_transact_sync_oneway(client, code, req);
reply = NULL;
} else {
reply = gbinder_client_transact_sync_reply(client, code, req, &status);
}
gbinder_local_request_unref(req);
if (!reply) {
printf("NO REPLY\n");
} else {
if (ast && !ast->tree_reply) {
guchar b;
gbinder_remote_reply_init_reader(reply, &app->reader);
printf("TRANSACTION BUFFER: 0x");
while (gbinder_reader_read_byte(&app->reader, &b)) {
printf("%02X", b);
}
printf("\n");
} else {
gbinder_remote_reply_init_reader(reply, &app->reader);
go_through_ast(app, ast, FALSE);
}
gbinder_remote_reply_unref(reply);
}
gbinder_client_unref(client);
free_ast(ast);
}
static
gboolean
app_log_verbose(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = (gutil_log_default.level < GLOG_LEVEL_DEBUG) ?
GLOG_LEVEL_DEBUG : GLOG_LEVEL_VERBOSE;
return TRUE;
}
static
gboolean
app_log_quiet(
const gchar* name,
const gchar* value,
gpointer data,
GError** error)
{
gutil_log_default.level = GLOG_LEVEL_ERR;
return TRUE;
}
static
gboolean
app_init(
AppOptions* opt,
int argc,
char* argv[])
{
gboolean ok = FALSE;
GOptionEntry entries[] = {
{ "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_verbose, "Enable verbose output", NULL },
{ "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
app_log_quiet, "Be quiet", NULL },
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
"Binder device [" DEFAULT_DEVICE "]", "DEVICE" },
{ "oneway", 'o', 0, G_OPTION_ARG_NONE, &opt->oneway,
"Use a oneway transaction", NULL },
{ "aidl", 'a', 0, G_OPTION_ARG_NONE, &opt->aidl,
"Treat types as aidl types (default: hidl)", NULL },
{ NULL }
};
GError* error = NULL;
GOptionContext* options = g_option_context_new("NAME CODE [[VALUE1] [VALUE2] ...] [reply [TYPE1] [TYPE2] ...]");
g_option_context_set_description(options, "This tool can be used to call binder transactions from the commandline.\n"
"After the regular options the NAME specifies the service name to call.\n"
"For example \"android.hardware.sensors@1.0::ISensors/default\".\n"
"CODE is used to specify the transaction id (>=1).\n"
"After the code arguments to the transaction can be specified.\n"
"Possible arguments are:\n"
"\t[0-9]*:8 for an 8-bit integer\n"
"\t[0-9]* for a 32-bit integer\n"
"\t[0-9]*L for an 64-bit integer\n"
"\t[0-9]*.[0-9]* for a 32-bit float\n"
"\t[0-9]*.[0-9]*L for a 64-bit double\n"
"\t\"[.*]\" for an 8-bit aidl string\n"
"\t\"[.*]\"L for an utf16 aidl string\n"
"\t\"[.*]\"h for an 8-bit hidl string\n"
"\t{ VALUE1 VALUE2 ... VALUEN } for a struct containing VALUE1, VALUE2, etc., where\n"
"\t all of these values can be any of the possible values described here.\n"
"\t[ VALUE1 VALUE2 ... VALUEN ] for a vector of length N containing VALUE1, VALUE2, etc., where\n"
"\t all of these values can be one of the possible VALUES described here.\n"
"\t They must be of the same type.\n"
"Afterwards the structure of the reply can be specified.\n"
"After writing \"reply\" on the command line the following types are accepted:\n"
"\ti8 for an 8-bit integer\n"
"\ti32 for a 32-bit integer\n"
"\ti64 for a 64-bit integer\n"
"\ts8 for an 8-bit aidl string\n"
"\ts16 for an utf16 aidl string\n"
"\thstr for an 8-bit hidl string\n"
"\tf|float for a 32-bit float\n"
"\td|double for a 64-bit double\n"
"\t[ TYPE ] for a vector<TYPE> where TYPE can be any of the possible types decribed here\n"
"\t{ TYPE1 TYPE2 ... TYPEN } for a struct containing TYPE1, TYPE2, etc. where\n"
"\t all of the types can be any of the possible types decribed here.\n"
"An example to call the getSensorsList method on the \"android.hardware.sensors@1.0::ISensors/default\"\n"
"service, looks like this:\n"
"\tbinder-call -d /dev/hwbinder android.hardware.sensors@1.0::ISensors/default 1 reply i32 \"[ { i32 i32 hstr hstr i32 i32 hstr f f f i32 i32 i32 hstr i32 i32 } ]\"\n");
g_option_context_add_main_entries(options, entries, NULL);
memset(opt, 0, sizeof(*opt));
gutil_log_timestamp = FALSE;
gutil_log_set_type(GLOG_TYPE_STDERR, pname);
gutil_log_default.level = GLOG_LEVEL_DEFAULT;
if (g_option_context_parse(options, &argc, &argv, &error)) {
char* help;
if (argc > 2) {
opt->argc = argc;
opt->argv = argv;
ok = TRUE;
} else {
help = g_option_context_get_help(options, TRUE, NULL);
fprintf(stderr, "%s", help);
g_free(help);
}
} else {
GERR("%s", error->message);
g_error_free(error);
}
g_option_context_free(options);
return ok;
}
int main(int argc, char* argv[])
{
App app;
AppOptions opt;
memset(&app, 0, sizeof(app));
app.ret = RET_INVARG;
app.opt = &opt;
if (app_init(&opt, argc, argv)) {
app.sm = gbinder_servicemanager_new(opt.dev);
if (app.sm) {
app_run(&app);
gbinder_servicemanager_unref(app.sm);
} else {
GERR("servicemanager seems to be missing");
}
}
g_free(opt.iface);
g_free(opt.dev);
return app.ret;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,63 @@
#ifndef BINDER_CALL_H__
#define BINDER_CALL_H__
#include <glib.h>
#include <gbinder.h>
typedef struct app_options {
char* dev;
char *iface;
gboolean oneway;
gboolean aidl;
gint transaction;
int argc;
char** argv;
} AppOptions;
typedef struct app {
const AppOptions* opt;
GMainLoop* loop;
GBinderServiceManager* sm;
GBinderWriter writer;
GBinderReader reader;
int code;
int rargc;
int ret;
} App;
int cmdlinelex(void *args);
enum TYPE_INFO {
INT8_TYPE = 0,
INT32_TYPE,
INT64_TYPE,
FLOAT_TYPE,
DOUBLE_TYPE,
STRING8_TYPE,
STRING16_TYPE,
HSTRING_TYPE,
STRUCT_TYPE,
VECTOR_TYPE
};
struct type_info {
enum TYPE_INFO type;
void *data;
};
struct value_info {
enum TYPE_INFO type;
void *value;
};
struct transaction_and_reply {
GList *tree_transaction;
GList *tree_reply;
};
int cmdline_parse(App *app);
extern struct transaction_and_reply *ast;
#endif

View File

@@ -0,0 +1,99 @@
D [0-9]
l "l"
L "L"
u "u"
U "U"
h "h"
H "H"
COLON ":"
EIGHT "8"
INT8_SUFFIX {COLON}{EIGHT}
INT64_SUFFIX [lL]
UTF16_SUFFIX [uU]
HSTRING_SUFFIX [hH]
%{
#include <stdio.h>
#include <glib.h>
#include "cmdline.tab.h"
#include "binder-call.h"
#define YY_NO_INPUT
#define YY_SKIP_YYWRAP
#define YY_NO_UNPUT
int cmdlinewrap(App* app);
#undef yywrap
#define yywrap() cmdlinewrap( (App*) args )
#define YY_DECL int cmdlinelex( void* args )
char* handle_str(char* text) {
// extract str from "str"X
char* str = g_strndup(text + 1, strlen(text) - 3);
return str;
}
char* handle_str8(char* text) {
// extract str from "str"
char* str = g_strndup(text + 1, strlen(text) - 2);
return str;
}
%}
%%
"i8" { return(INT8); }
"i32" { return(INT32); }
"i64" { return(INT64); }
"s8" { return(STRING8); }
"s16" { return(STRING16); }
"float" { return(FLOAT); }
"double" { return(DOUBLE); }
"f" { return(FLOAT); }
"d" { return(DOUBLE); }
"hstr" { return(HSTRING); }
"{" { return('{'); }
"}" { return('}'); }
"[" { return('['); }
"]" { return(']'); }
{D}*{INT8_SUFFIX} { cmdlinelval.int8_value = atoi(yytext); return(INT8_VALUE); }
{D}*{INT64_SUFFIX} { cmdlinelval.int64_value = atol(yytext); return(INT64_VALUE); }
{D}* { cmdlinelval.int32_value = atoi(yytext); return(INT32_VALUE); }
{D}+"."{D}*{INT64_SUFFIX} { cmdlinelval.double_value = atof(yytext); return(DOUBLE_VALUE); }
{D}+"."{D}* { cmdlinelval.float_value = atof(yytext); return(FLOAT_VALUE); }
"reply" { return(REPLY); }
\".*\"{HSTRING_SUFFIX} { cmdlinelval.hstring_value = handle_str(yytext); return(HSTRING_VALUE); }
\".*\"{UTF16_SUFFIX} { cmdlinelval.string16_value = handle_str(yytext); return(STRING16_VALUE); }
\".*\" { cmdlinelval.string8_value = handle_str8(yytext); return(STRING8_VALUE); }
" " { /* eat */ }
. { fprintf(stderr, "Unrecognized character: '%c'\n", yytext[0]); }
%%
#include "binder-call.h"
int cmdlinewrap(App* app)
{
if (YY_CURRENT_BUFFER) {
yy_delete_buffer( YY_CURRENT_BUFFER );
}
if (app->rargc == app->opt->argc) {
return 1;
}
yy_scan_string(app->opt->argv[app->rargc++]);
return 0;
}
int cmdline_parse(App* app) {
if (app->opt->argc > app->rargc) {
cmdlinewrap(app);
} else {
return 1;
}
return cmdlineparse (app);
}

402
test/binder-call/cmdline.y Normal file
View File

@@ -0,0 +1,402 @@
%{
#include <glib.h>
#include "binder-call.h"
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply);
struct value_info* handle_int8(App* app, int value);
struct value_info* handle_int32(App* app, int value);
struct value_info* handle_int64(App* app, long value);
struct value_info* handle_float(App* app, float value);
struct value_info* handle_double(App* app, double value);
struct value_info* handle_string8(App* app, char* value);
struct value_info* handle_string16(App* app, char* value);
struct value_info* handle_hstring(App* app, char* value);
struct value_info* handle_vector(App* app, GList* values);
struct value_info* handle_struct(App* app, GList* values);
void cmdlineerror(App* app, char const* s);
struct type_info* handle_type_int8(App* app);
struct type_info* handle_type_int32(App* app);
struct type_info* handle_type_int64(App* app);
struct type_info* handle_type_float(App* app);
struct type_info* handle_type_double(App* app);
struct type_info* handle_type_string8(App* app);
struct type_info* handle_type_string16(App* app);
struct type_info* handle_type_hstring(App* app);
struct type_info* handle_type_vector(App* app, struct type_info* t);
struct type_info* handle_type_struct(App* app, GList* l);
%}
%union {
union {
int int8_value;
int int32_value;
long int64_value;
float float_value;
double double_value;
char* string8_value;
char* string16_value;
char* hstring_value;
};
struct value_info* value;
struct type_info* type;
GList* value_list;
GList* type_list;
GList* struct_type_list;
struct transaction_and_reply* trans_and_reply;
}
%parse-param { void* args }
%lex-param { void* args }
%token INT8 INT32 INT64 FLOAT DOUBLE STRING8 STRING16 HSTRING
%token INT8_VALUE INT32_VALUE INT64_VALUE FLOAT_VALUE DOUBLE_VALUE STRING8_VALUE STRING16_VALUE HSTRING_VALUE
%type <value> values
%type <value> struct_values
%type <value> vec_values
%type <value> value_specifiers
%type <trans_and_reply> translation_unit
%type <value_list> values_list
%type <type_list> specifiers_list
%type <type> specifiers
%type <type> type_specifier
%type <type> vec_specifier
%type <type> struct_specifier
%type <struct_type_list> struct_declaration_list
//%type <value> values
%token REPLY
%start translation_unit
%%
type_specifier
: INT8 { $$ = handle_type_int8(args); }
| INT32 { $$ = handle_type_int32(args); }
| INT64 { $$ = handle_type_int64(args); }
| STRING8 { $$ = handle_type_string8(args); }
| STRING16 { $$ = handle_type_string16(args); }
| FLOAT { $$ = handle_type_float(args); }
| DOUBLE { $$ = handle_type_double(args); }
| HSTRING { $$ = handle_type_hstring(args); }
;
values
: INT8_VALUE { $$ = handle_int8(args, cmdlinelval.int8_value); }
| INT32_VALUE { $$ = handle_int32(args, cmdlinelval.int32_value); }
| INT64_VALUE { $$ = handle_int64(args, cmdlinelval.int64_value); }
| STRING8_VALUE { $$ = handle_string8(args, cmdlinelval.string8_value); }
| STRING16_VALUE { $$ = handle_string16(args, cmdlinelval.string16_value); }
| HSTRING_VALUE { $$ = handle_hstring(args, cmdlinelval.hstring_value); }
| FLOAT_VALUE { $$ = handle_float(args, cmdlinelval.float_value); }
| DOUBLE_VALUE { $$ = handle_double(args, cmdlinelval.double_value); }
;
struct
: '{'
;
struct_end
: '}'
;
vec
: '['
;
vec_end
: ']'
;
struct_specifier
: struct struct_declaration_list struct_end { $$ = handle_type_struct(args, $2); }
;
vec_specifier
: vec specifiers vec_end { $$ = handle_type_vector(args, $2); }
;
struct_declaration_list
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| struct_declaration_list specifiers { $$ = g_list_append($$, $2); }
;
specifiers
: type_specifier
| struct_specifier
| vec_specifier
;
specifiers_list
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| specifiers_list specifiers { $$ = g_list_append($$, $2); }
;
struct_values
: struct values_list struct_end { $$ = handle_struct(args, $2); }
;
vec_values
: vec values_list vec_end { $$ = handle_vector(args, $2); }
;
value_specifiers
: values
| struct_values
| vec_values
;
values_list
: value_specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
| values_list value_specifiers { $$ = g_list_append($$, $2); }
;
reply
: REPLY
;
translation_unit
: values_list reply specifiers_list { $$ = make_transaction_and_reply($1, $3); ast = $$; }
| values_list { $$ = make_transaction_and_reply($1, 0); ast = $$; }
| reply specifiers_list { $$ = make_transaction_and_reply(0, $2); ast = $$; }
;
%%
#include <stdio.h>
#include <glib.h>
#include <gutil_log.h>
#include <binder-call.h>
extern char yytext[];
struct value_info* handle_int8(App* app, int value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT8_TYPE;
v->value = g_new0(unsigned char, 1);
* ((unsigned char*)v->value) = value;
return v;
}
struct value_info* handle_int32(App* app, int value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT32_TYPE;
v->value = g_new0(int, 1);
* ((int*)v->value) = value;
return v;
}
struct value_info* handle_int64(App* app, long value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = INT64_TYPE;
v->value = g_new0(long, 1);
* ((long*)v->value) = value;
return v;
}
struct value_info* handle_float(App* app, float value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = FLOAT_TYPE;
v->value = g_new0(float, 1);
* ((float*)v->value) = value;
return v;
}
struct value_info* handle_double(App* app, double value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = DOUBLE_TYPE;
v->value = g_new0(double, 1);
* ((double*)v->value) = value;
return v;
}
struct value_info* handle_string8(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRING8_TYPE;
v->value = value;
return v;
}
struct value_info* handle_string16(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRING16_TYPE;
v->value = value;
return v;
}
struct value_info* handle_hstring(App* app, char* value)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = HSTRING_TYPE;
v->value = value;
return v;
}
struct value_info* handle_vector(App* app, GList* values)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = VECTOR_TYPE;
v->value = values;
return v;
}
struct value_info* handle_struct(App* app, GList* values)
{
struct value_info* v = g_new0(struct value_info, 1);
v->type = STRUCT_TYPE;
v->value = values;
return v;
}
struct type_info* handle_type_int8(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT8_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_int32(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT32_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_int64(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = INT64_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_float(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = FLOAT_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_double(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = DOUBLE_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_string8(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRING8_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_string16(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRING16_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_hstring(App* app)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = HSTRING_TYPE;
info->data = 0;
return info;
}
struct type_info* handle_type_vector(App* app, struct type_info* t)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = VECTOR_TYPE;
info->data = t;
return info;
}
struct type_info* handle_type_struct(App* app, GList* l)
{
struct type_info* info = g_new0(struct type_info, 1);
info->type = STRUCT_TYPE;
info->data = l;
return info;
}
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply)
{
struct transaction_and_reply* tar = g_new0(struct transaction_and_reply, 1);
tar->tree_transaction = transaction;
tar->tree_reply = reply;
return tar;
}
void cmdlineerror(App* app, char const* s)
{
fprintf(stderr, "@%d %s: %s\n", app->rargc - 1, s, app->opt->argv[app->rargc - 1]);
}