aml-dbus: add aml-dbus source [1/1]

PD#SWPL-155651

Problem:
add aml-dbus source

Solution:
add aml-dbus source

Verify:
local

Change-Id: I02132b92e0841b577ecb9467f9b73e6b159dec1a
Signed-off-by: Daogao Xu <daogao.xu@amlogic.com>
diff --git a/aml_dbus/Makefile b/aml_dbus/Makefile
new file mode 100644
index 0000000..4ebaae5
--- /dev/null
+++ b/aml_dbus/Makefile
@@ -0,0 +1,66 @@
+LDLIBS += -lsystemd -lpthread
+LINK:=$(CC)
+
+LIB_VER=0
+
+# BUILDDIR used for out-of-source compile
+BUILDDIR?=./
+
+all:
+
+.PHONY: clean
+
+define build_project
+OBJ_C := $(patsubst %.c,$(TARGET)_o/%.o,$(filter %.c,$2))
+OBJ_CXX := $(patsubst %.cpp,$(TARGET)_o/%.o,$(filter %.cpp,$2))
+OBJ_PATH := $(addprefix $(TARGET)_o/, $(sort $(dir $2)))
+_dummy := $$(shell mkdir -p $$(OBJ_PATH))
+$(TARGET):OBJ_ALL:=$$(OBJ_C) $$(OBJ_CXX)
+$(TARGET):LINK:=$(if $(filter %.cpp,$2),$(CXX),$(CC))
+$(TARGET)_clean:CLEAN_FILES:=$(TARGET) $$(OBJ_PATH) $(TARGET)_o
+ifneq (,$(findstring shared_lib,$1))
+ifneq ($(suffix $(TARGET)),.so)
+all:$(basename $(TARGET))
+$(basename $(TARGET)):$(TARGET)
+	ln -frs $$< $$@
+endif
+endif
+$(TARGET): $$(OBJ_C) $$(OBJ_CXX)
+ifneq (,$(findstring shared_lib,$1))
+	$$(LINK) $$(CPPFLAGS) $$(LDFLAGS) -shared -Wl,-soname,$(notdir $(TARGET)) -o $$@ $$(OBJ_ALL) $$(LDLIBS)
+else ifneq (,$(findstring static_lib,$1))
+	$(AR) rcs -o $$@ $$+
+	$(RANLIB) $$@
+else
+	$$(LINK) $$(CPPFLAGS) $$(LDFLAGS) -o $$@ $$(OBJ_ALL) $$(LDLIBS)
+endif
+
+$$(OBJ_C):$(TARGET)_o/%.o:%.c
+	$$(CC) $$(CPPFLAGS) $$(CFLAGS) -c $$< -o $$@
+
+$$(OBJ_CXX):$(TARGET)_o/%.o:%.cpp
+	$$(CXX) $$(CPPFLAGS) $$(CXXFLAGS) -c $$< -o $$@
+
+.PHONY: $(TARGET)_clean
+
+$(TARGET)_clean:
+	-rm -rf $$(abspath $$(CLEAN_FILES))
+
+clean: $(TARGET)_clean
+
+all:$(TARGET)
+
+endef
+
+TARGET := $(BUILDDIR)/libamldbus.so.$(LIB_VER)
+$(eval $(call build_project,shared_lib,aml-dbus.c))
+
+TARGET := $(BUILDDIR)/aml-dbus-test
+$(eval $(call build_project,exe,aml-dbus-test.cpp aml-dbus.c))
+
+TARGET := $(BUILDDIR)/ambus-sample-server
+$(eval $(call build_project,exe,sample-server.c aml-dbus.c))
+
+TARGET := $(BUILDDIR)/ambus-sample-client
+$(eval $(call build_project,exe,sample-client.c aml-dbus.c))
+
diff --git a/aml_dbus/aml-dbus-generator.h b/aml_dbus/aml-dbus-generator.h
new file mode 100644
index 0000000..9bc7abc
--- /dev/null
+++ b/aml_dbus/aml-dbus-generator.h
@@ -0,0 +1,114 @@
+#ifndef AMBUS_GENERATOR
+#define AMBUS_GENERATOR
+#endif
+
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#define AMBUS_BEGIN_INTF(SERVICE, OBJ, INTF) enum {
+#define AMBUS_PROP(_member, _signature) MACRO_CAT2(AMBUS_INTERFACE_ID, _member##_enum),
+#define AMBUS_PROPRW(_member, _signature) MACRO_CAT2(AMBUS_INTERFACE_ID, _member##_enum),
+#define AMBUS_METHOD(_member, _signature, _result) MACRO_CAT2(AMBUS_INTERFACE_ID, _member##_enum),
+#define AMBUS_SIGNAL(_member, _signature) MACRO_CAT2(AMBUS_INTERFACE_ID, _member##_enum),
+#define AMBUS_END_INTF()                                                                                               \
+  MACRO_CAT2(AMBUS_INTERFACE_ID, _END)                                                                                 \
+  }                                                                                                                    \
+  ;
+#include AMBUS_INTERFACE_FILE
+
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#define AMBUS_BEGIN_INTF(SERVICE, OBJ, INTF) extern struct ambus_interface MACRO_CAT2(AMBUS_INTERFACE_ID, _interface);
+#define AMBUS_PROP(_member, _signature)
+#define AMBUS_PROPRW(_member, _signature)
+#define AMBUS_METHOD(_member, _signature, _result)
+#define AMBUS_SIGNAL(_member, _signature)
+#define AMBUS_END_INTF()
+#include AMBUS_INTERFACE_FILE
+
+#ifdef AMBUS_GENERATE_INTERFACE
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#define AMBUS_BEGIN_INTF(SERVICE, OBJ, INTF)                                                                           \
+  struct ambus_interface MACRO_CAT2(AMBUS_INTERFACE_ID, _interface) = {SERVICE, OBJ, INTF, {
+#define AMBUS_PROP(_member, _signature) {#_member, _signature, NULL},
+#define AMBUS_PROPRW(_member, _signature) {#_member, _signature, NULL},
+#define AMBUS_METHOD(_member, _signature, _result) {#_member, _signature, _result},
+#define AMBUS_SIGNAL(_member, _signature) {#_member, _signature, NULL},
+#define AMBUS_END_INTF()                                                                                               \
+  { NULL, NULL, NULL }                                                                                                 \
+  }                                                                                                                    \
+  }                                                                                                                    \
+  ;
+#include AMBUS_INTERFACE_FILE
+#endif // AMBUS_GENERATE_INTERFACE
+
+#ifdef AMBUS_GENERATE_VTABLE
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#define AMBUS_BEGIN_INTF(SERVICE, OBJ, INTF)
+#define AMBUS_PROP(_member, _signature)                                                                                \
+  static int MACRO_CAT2(AMBUS_INTERFACE_ID, _property_get)(sd_bus * bus, const char *path, const char *interface,      \
+                                                           const char *property, sd_bus_message *reply,                \
+                                                           void *userdata, sd_bus_error *ret_error);
+#define AMBUS_PROPRW(_member, _signature)                                                                              \
+  static int MACRO_CAT2(AMBUS_INTERFACE_ID, _property_get)(sd_bus * bus, const char *path, const char *interface,      \
+                                                           const char *property, sd_bus_message *reply,                \
+                                                           void *userdata, sd_bus_error *ret_error);                   \
+  static int MACRO_CAT2(AMBUS_INTERFACE_ID, _property_set)(sd_bus * bus, const char *path, const char *interface,      \
+                                                           const char *property, sd_bus_message *value,                \
+                                                           void *userdata, sd_bus_error *ret_error);
+#define AMBUS_METHOD(_member, _signature, _result)                                                                     \
+  static int MACRO_CAT2(AMBUS_INTERFACE_ID, _method_##_member)(sd_bus_message * m, void *userdata,                     \
+                                                               sd_bus_error *ret_error);
+#define AMBUS_SIGNAL(_member, _signature)
+#define AMBUS_END_INTF()
+#include AMBUS_INTERFACE_FILE
+
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#define AMBUS_BEGIN_INTF(SERVICE, OBJ, INTF)                                                                           \
+  static const sd_bus_vtable MACRO_CAT2(AMBUS_INTERFACE_ID, _vtable)[] = {SD_BUS_VTABLE_START(0),
+#define AMBUS_PROP(_member, _signature)                                                                                \
+  SD_BUS_PROPERTY(#_member, _signature, MACRO_CAT2(AMBUS_INTERFACE_ID, _property_get), 0, 0),
+#define AMBUS_PROPRW(_member, _signature)                                                                              \
+  SD_BUS_WRITABLE_PROPERTY(#_member, _signature, MACRO_CAT2(AMBUS_INTERFACE_ID, _property_get),                        \
+                           MACRO_CAT2(AMBUS_INTERFACE_ID, _property_set), 0, 0),
+#define AMBUS_METHOD(_member, _signature, _result)                                                                     \
+  SD_BUS_METHOD(#_member, _signature, _result, MACRO_CAT2(AMBUS_INTERFACE_ID, _method_##_member),                      \
+                SD_BUS_VTABLE_UNPRIVILEGED),
+#define AMBUS_SIGNAL(_member, _signature) SD_BUS_SIGNAL(#_member, _signature, 0),
+#define AMBUS_END_INTF()                                                                                               \
+  SD_BUS_VTABLE_END                                                                                                    \
+  }                                                                                                                    \
+  ;
+#include AMBUS_INTERFACE_FILE
+#endif // AMBUS_GENERATE_VTABLE
+
+#undef AMBUS_BEGIN_INTF
+#undef AMBUS_PROP
+#undef AMBUS_PROPRW
+#undef AMBUS_METHOD
+#undef AMBUS_SIGNAL
+#undef AMBUS_END_INTF
+#undef AMBUS_GENERATOR
+
diff --git a/aml_dbus/aml-dbus-test.cpp b/aml_dbus/aml-dbus-test.cpp
new file mode 100644
index 0000000..6b0e544
--- /dev/null
+++ b/aml_dbus/aml-dbus-test.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014-2019 Amlogic, Inc. All rights reserved.
+ *
+ * All information contained herein is Amlogic confidential.
+ *
+ * This software is provided to you pursuant to Software License Agreement
+ * (SLA) with Amlogic Inc ("Amlogic"). This software may be used
+ * only in accordance with the terms of this agreement.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification is strictly prohibited without prior written permission from
+ * Amlogic.
+ *
+ * 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
+ * OWNER 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 "aml-dbus.h"
+#include <ctype.h>
+#include <string>
+#include <vector>
+
+const char *test_service = "amlogic.yocto.test";
+const char *test_object = "/amlogic/yocto/test/obj1";
+const char *test_interface = "amlogic.yocto.test.intf1";
+
+struct TestStruct {
+  std::string name;
+  int ival;
+  float fval;
+  bool bval;
+};
+
+int test_func1(int a1, const int a2, const int &a3, int &a4) {
+  a4 = a1 + a2 + a3;
+  int ret = a1;
+  fprintf(stderr, "call test_func1(a1=%d,a2=%d, a3=%d, a4=%d, ret=%d\n", a1, a2, a3, a4, ret);
+  return ret;
+}
+
+int test_func2(const std::string &arg1, std::vector<std::string> &out1, TestStruct & out2) {
+  fprintf(stderr, "call test_func2 %s\n", arg1.c_str());
+  out1 = {"1", "2", "3", arg1};
+  out2 = {arg1, arg1.size(), arg1.size() * 1.0, isdigit(arg1[0])};
+  return 0;
+}
+
+std::unordered_map<std::string, TestStruct> test_func3() {
+  return {{"key1", {"n1", 10, 0.123, true}}, {"key2", {"n2", 20, 0.456, false}}};
+}
+
+AMBUS_STRUCT_DEFINE(TestStruct, name, ival, fval, bval);
+
+static int ambus_method_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+  struct ambus_method_call<> *pcall = (struct ambus_method_call<> *)userdata;
+  return pcall->call(m);
+}
+
+#define NEW_METHOD_ITEM(P, f, ...) new ambus_method_call<decltype(&f)>(#f, &f),
+#define NEW_METHOD_VTABLE(P, f, ...)                                                                                   \
+  SD_BUS_METHOD_WITH_OFFSET(#f, ambus_method_call<decltype(&f)>::data_pack::signature_input::value,                    \
+                            ambus_method_call<decltype(&f)>::signature_output::value, ambus_method_handler,            \
+                            (size_t)*P##_methods_ptr++, 0),
+
+#define REGISTER_DBUS_METHOD(P, ...)                                                                                   \
+  static const ambus_method_call<> *P##_methods[] = {MACRO_MAP(NEW_METHOD_ITEM, P, __VA_ARGS__)},                      \
+                                   **P##_methods_ptr = P##_methods;                                                    \
+  static const sd_bus_vtable P##_vtable[] = {SD_BUS_VTABLE_START(0),                                                   \
+                                             MACRO_MAP(NEW_METHOD_VTABLE, P, __VA_ARGS__) SD_BUS_VTABLE_END};
+
+#define CALL_DBUS(f, ...)                                                                                              \
+  ambus_method_proxy<decltype(&f)>::call(ambus, test_service, test_object, test_interface, #f, ##__VA_ARGS__)
+
+REGISTER_DBUS_METHOD(test, test_func1, test_func2, test_func3);
+// above macro will generate code similar as the comment line below:
+#if 0
+static const ambus_method_call<> *test_methods[] = {
+    new ambus_method_call<decltype(&test_func1)>("test_func1", &test_func1),
+    new ambus_method_call<decltype(&test_func2)>("test_func2", &test_func2),
+};
+
+static const sd_bus_vtable test_vtable[] = {
+    SD_BUS_VTABLE_START(0),
+    SD_BUS_METHOD_WITH_OFFSET("test_func1", ambus_method_call<decltype(&test_func1)>::data_pack::signature_input::value,
+                              ambus_method_call<decltype(&test_func1)>::signature_output::value, ambus_method_handler,
+                              (size_t)test_methods[0], 0),
+    SD_BUS_METHOD_WITH_OFFSET("test_func2", ambus_method_call<decltype(&test_func2)>::data_pack::signature_input::value,
+                              ambus_method_call<decltype(&test_func2)>::signature_output::value, ambus_method_handler,
+                              (size_t)test_methods[1], 0),
+    SD_BUS_VTABLE_END};
+
+#endif
+
+int main(int argc, char *argv[]) {
+  int r;
+  if (argc != 2) {
+    printf("USAGE: %s server/client\n", argv[0]);
+    return 1;
+  }
+  if (strcmp(argv[1], "server") == 0) {
+    struct aml_dbus *ambus = ambus_new(NULL, 0);
+    sd_bus_add_object_vtable(ambus_sdbus(ambus), NULL, test_object, test_interface, test_vtable, NULL);
+    sd_bus_request_name(ambus_sdbus(ambus), test_service, 0LL);
+    ambus_run(ambus);
+  } else if (strcmp(argv[1], "client") == 0) {
+    struct aml_dbus *ambus = ambus_ensure_run(NULL);
+    int a1 = 1;
+    int a2 = 2;
+    const int a3 = 10;
+    int a4 = 0;
+    // int r = ambus_method_proxy<decltype(&test_func1)>::call(ambus, test_service, test_object,
+    // test_interface, "test_func1", a1, a2, a3, a4);
+    int r = CALL_DBUS(test_func1, a1, a2, a3, a4);
+    printf("call test_func1 %d %d %d %d ret %d\n", a1, a2, a3, a4, r);
+
+    const std::string arg1 = "arg1";
+    std::vector<std::string> out1;
+    TestStruct out2;
+    r = ambus_method_proxy<decltype(&test_func2)>::call(ambus, test_service, test_object, test_interface, "test_func2",
+                                                        arg1, out1, out2);
+    printf("call test_func2(%s) ret %d, out2: name:%s ival:%d fval:%f bval:%d out1:", arg1.c_str(), r,
+           out2.name.c_str(), out2.ival, out2.fval, out2.bval);
+    for (auto &e : out1)
+      printf("%s ", e.c_str());
+    printf("\n");
+
+    const std::unordered_map<std::string, TestStruct> &out3 = CALL_DBUS(test_func3);
+    printf("call test_func3 return ");
+    for (auto &e : out3)
+      printf("%s:(%s,%d,%f,%d),", e.first.c_str(), e.second.name.c_str(), e.second.ival, e.second.fval, e.second.bval);
+    printf("\n");
+  }
+  return 0;
+}
diff --git a/aml_dbus/aml-dbus.c b/aml_dbus/aml-dbus.c
new file mode 100644
index 0000000..77b9cba
--- /dev/null
+++ b/aml_dbus/aml-dbus.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2014-2019 Amlogic, Inc. All rights reserved.
+ *
+ * All information contained herein is Amlogic confidential.
+ *
+ * This software is provided to you pursuant to Software License Agreement
+ * (SLA) with Amlogic Inc ("Amlogic"). This software may be used
+ * only in accordance with the terms of this agreement.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification is strictly prohibited without prior written permission from
+ * Amlogic.
+ *
+ * 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
+ * OWNER 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 "aml-dbus.h"
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <time.h>
+#include <unistd.h>
+
+struct delay_task {
+  struct delay_task *next;
+  void (*cb)(void *);
+  void *param;
+  sd_event_source *src;
+  struct aml_dbus *ambus;
+  uint32_t delay_ms;
+};
+
+struct aml_dbus {
+  sd_bus *bus;
+  sd_event *event;
+  pthread_mutex_t lock;
+  pthread_cond_t cond;
+  pthread_t dispatch_thread;
+  int msgpipe[2];
+  struct delay_task *free_task;
+};
+
+static int msgpipe_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+  void *args[2];
+  int len = read(fd, &args, sizeof(args));
+  if (len != sizeof(args)) {
+    AMBUS_LOGW("read %d bytes return %d, errno %d %s", (int)sizeof(args), len, errno, strerror(errno));
+  } else
+    ((void (*)(void *))args[0])(args[1]);
+  return 0;
+}
+
+int ambus_post_task(struct aml_dbus *ambus, void (*cb)(void *), void *param) {
+  void *args[] = {cb, param};
+  int len = write(ambus->msgpipe[1], args, sizeof(args));
+  if (len != sizeof(args)) {
+    AMBUS_LOGW("write %d bytes return %d, errno %d %s", (int)sizeof(args), len, errno, strerror(errno));
+    return -1;
+  }
+  return 0;
+}
+
+static int delay_task_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+  struct delay_task *task = (struct delay_task *)userdata;
+  struct aml_dbus *ambus = task->ambus;
+  pthread_mutex_lock(&ambus->lock);
+  task->next = ambus->free_task;
+  ambus->free_task = task;
+  pthread_mutex_unlock(&ambus->lock);
+  task->cb(task->param);
+  return 0;
+}
+
+static void delay_task_settimer(void *param) {
+  struct delay_task *task = (struct delay_task *)param;
+  struct aml_dbus *ambus = task->ambus;
+  uint64_t timeout;
+  int r = sd_event_now(ambus->event, CLOCK_MONOTONIC, &timeout);
+  timeout += task->delay_ms * 1000LL;
+  if (task->src)
+    r = sd_event_source_set_time(task->src, timeout);
+  else
+    r = sd_event_add_time(ambus->event, &task->src, CLOCK_MONOTONIC, timeout, 1000LL * 10, delay_task_timeout, task);
+  if (r >= 0)
+    sd_event_source_set_enabled(task->src, SD_EVENT_ONESHOT);
+}
+
+int ambus_post_delay_task(struct aml_dbus *ambus, uint32_t delay_ms, void (*cb)(void *), void *param) {
+  if (delay_ms == 0)
+    return ambus_post_task(ambus, cb, param);
+  pthread_mutex_lock(&ambus->lock);
+  struct delay_task *task = ambus->free_task;
+  if (task)
+    ambus->free_task = task->next;
+  else {
+    task = calloc(1, sizeof(*task));
+    task->ambus = ambus;
+  }
+  pthread_mutex_unlock(&ambus->lock);
+  task->cb = cb;
+  task->param = param;
+  task->delay_ms = delay_ms;
+  return ambus_post_task(ambus, delay_task_settimer, task);
+}
+
+void ambus_free(struct aml_dbus *ambus) {
+  if (!ambus_in_dispatch_thread(ambus)) {
+    POST_AND_WAIT_DISPATCH(ambus, ambus_free(ambus));
+    pthread_join(ambus->dispatch_thread, NULL);
+    free(ambus);
+  } else {
+    if (ambus->event) {
+      sd_event_exit(ambus->event, 0);
+      sd_event_unref(ambus->event);
+    }
+    if (ambus->bus)
+      sd_bus_unref(ambus->bus);
+    if (ambus->msgpipe[0])
+      close(ambus->msgpipe[0]);
+    if (ambus->msgpipe[1])
+      close(ambus->msgpipe[1]);
+  }
+}
+
+struct aml_dbus *ambus_new(const char *address, int mode) {
+  struct aml_dbus *ambus = calloc(1, sizeof(*ambus));
+  ambus->dispatch_thread = pthread_self();
+  int r;
+  if (address == AMBUS_DEFAULT_SYSTEM || address == NULL) {
+    AMBUS_CHECK(r, sd_event_default(&ambus->event), goto fail);
+    AMBUS_CHECK(r, sd_bus_default_system(&ambus->bus), goto fail);
+  } else if (address == AMBUS_DEFAULT_USER) {
+    AMBUS_CHECK(r, sd_event_default(&ambus->event), goto fail);
+    AMBUS_CHECK(r, sd_bus_default_user(&ambus->bus), goto fail);
+  } else {
+    AMBUS_CHECK(r, sd_event_new(&ambus->event), goto fail);
+    if (address != AMBUS_DEFAULT_NONE) {
+      AMBUS_CHECK(r, sd_bus_new(&ambus->bus), goto fail);
+      AMBUS_CHECK(r, sd_bus_set_address(ambus->bus, address), goto fail);
+      AMBUS_CHECK(r, sd_bus_start(ambus->bus), goto fail);
+    }
+  }
+  pthread_mutex_init(&ambus->lock, NULL);
+  pthread_cond_init(&ambus->cond, NULL);
+  if (ambus->bus) {
+    AMBUS_CHECK(r, sd_bus_attach_event(ambus->bus, ambus->event, 0), goto fail);
+  }
+  AMBUS_CHECK(r, pipe(ambus->msgpipe), goto fail);
+  AMBUS_CHECK(r, sd_event_add_io(ambus->event, NULL, ambus->msgpipe[0], POLLIN, msgpipe_handler, ambus), goto fail);
+  return ambus;
+fail:
+  ambus_free(ambus);
+  return NULL;
+}
+
+static pthread_mutex_t global_ambus_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t global_ambus_cond = PTHREAD_COND_INITIALIZER;
+static void *ambus_dispatcher(void *param) {
+  struct aml_dbus *ambus = ambus_new(NULL, 0);
+  pthread_mutex_lock(&global_ambus_mutex);
+  *(struct aml_dbus **)param = ambus;
+  pthread_cond_broadcast(&global_ambus_cond);
+  pthread_mutex_unlock(&global_ambus_mutex);
+  ambus_run(ambus);
+  return NULL;
+}
+
+struct aml_dbus *ambus_ensure_run(struct aml_dbus **ppambus) {
+  static struct aml_dbus *ambus_invalid = (struct aml_dbus *)-1;
+  struct aml_dbus *tmpbus = NULL;
+  if (ppambus == NULL)
+    ppambus = &tmpbus;
+  if (*ppambus == NULL || *ppambus == ambus_invalid) {
+    pthread_mutex_lock(&global_ambus_mutex);
+    if (*ppambus == NULL) {
+      pthread_t t;
+      *ppambus = ambus_invalid;
+      pthread_create(&t, NULL, ambus_dispatcher, ppambus);
+    }
+    while (*ppambus == ambus_invalid)
+      pthread_cond_wait(&global_ambus_cond, &global_ambus_mutex);
+    pthread_mutex_unlock(&global_ambus_mutex);
+  }
+  return *ppambus;
+}
+
+int ambus_run(struct aml_dbus *ambus) {
+  ambus->dispatch_thread = pthread_self();
+  return sd_event_loop(ambus->event);
+}
+
+sd_bus *ambus_sdbus(struct aml_dbus *ambus) { return ambus->bus; }
+
+sd_event *ambus_sdevent(struct aml_dbus *ambus) { return ambus->event; }
+
+bool ambus_in_dispatch_thread(struct aml_dbus *ambus) { return pthread_equal(pthread_self(), ambus->dispatch_thread); }
+
+void ambus_wait_flag(struct aml_dbus *ambus, uint32_t *val, uint32_t mask) {
+  pthread_mutex_lock(&ambus->lock);
+  while ((*val & mask) == 0)
+    pthread_cond_wait(&ambus->cond, &ambus->lock);
+  pthread_mutex_unlock(&ambus->lock);
+}
+
+void ambus_set_flag(struct aml_dbus *ambus, uint32_t *val, uint32_t mask) {
+  pthread_mutex_lock(&ambus->lock);
+  *val |= mask;
+  pthread_cond_broadcast(&ambus->cond);
+  pthread_mutex_unlock(&ambus->lock);
+}
+
+int ambus_call_async(struct aml_dbus *ambus, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata) {
+  int r = -1;
+  RUN_IN_DISPATCH_THREAD(ambus, r = sd_bus_call_async(ambus->bus, NULL, m, callback, userdata, 0));
+  return r;
+}
+
+sd_bus_message *ambus_call_sync(struct aml_dbus *ambus, sd_bus_message *m) {
+  int r = -1;
+  sd_bus_message *ret = NULL;
+  if (ambus_in_dispatch_thread(ambus)) {
+    AMBUS_LOGW("cannot make sync call in dispatch thread");
+    return NULL;
+  } else {
+    int done = 0;
+    int method_done(sd_bus_message * dm, void *userdata, sd_bus_error *ret_error) {
+      ret = sd_bus_message_ref(dm);
+      AMBUS_LOGD("call done, notify caller");
+      ambus_set_flag(ambus, &done, 1);
+      return 0;
+    }
+    POST_AND_WAIT_DISPATCH(ambus, r = sd_bus_call_async(ambus->bus, NULL, m, method_done, NULL, 0));
+    if (r >= 0)
+      ambus_wait_flag(ambus, &done, 1);
+  }
+  return ret;
+}
+
+int ambus_call_simple_async_va(struct aml_dbus *ambus, struct ambus_interface *intf, int idx,
+                               sd_bus_message_handler_t callback, void *userdata, va_list ap) {
+  int r = -1;
+  int done = 0;
+  void do_task(void *p) {
+    sd_bus_message *msg = NULL;
+    r = sd_bus_message_new_method_call(ambus->bus, &msg, intf->service, intf->object, intf->interface,
+                                       intf->vtable[idx].member);
+    if (r >= 0 && intf->vtable[idx].signature)
+      r = sd_bus_message_appendv(msg, intf->vtable[idx].signature, ap);
+    if (r >= 0)
+      r = sd_bus_call_async(ambus->bus, NULL, msg, callback, userdata, 0);
+    ambus_set_flag(ambus, &done, 1);
+  }
+  if (ambus_in_dispatch_thread(ambus))
+    do_task(NULL);
+  else if (ambus_post_task(ambus, do_task, NULL) == 0)
+    ambus_wait_flag(ambus, &done, 1);
+  return r;
+}
+
+int ambus_call_simple_async(struct aml_dbus *ambus, struct ambus_interface *intf, int idx,
+                            sd_bus_message_handler_t callback, void *userdata, ...) {
+  va_list ap;
+  va_start(ap, userdata);
+  int r = ambus_call_simple_async_va(ambus, intf, idx, callback, userdata, ap);
+  va_end(ap);
+  return r;
+}
+
+int ambus_call_simple_sync_va(struct aml_dbus *ambus, struct ambus_interface *intf, int idx, va_list ap) {
+  if (ambus_in_dispatch_thread(ambus)) {
+    AMBUS_LOGE("cannot make sync call in dispatch thread");
+    return -1;
+  }
+  int r;
+  int done = 0;
+  int msg_handle(sd_bus_message * m, void *userdata, sd_bus_error *ret_error) {
+    if (intf->vtable[idx].result)
+      r = sd_bus_message_readv(m, intf->vtable[idx].result, ap);
+    ambus_set_flag(ambus, &done, 1);
+    return 0;
+  }
+  r = ambus_call_simple_async_va(ambus, intf, idx, msg_handle, NULL, ap);
+  va_end(ap);
+  if (r >= 0)
+    ambus_wait_flag(ambus, &done, 1);
+  return r;
+}
+
+int ambus_call_simple_sync(struct aml_dbus *ambus, struct ambus_interface *intf, int idx, ...) {
+  va_list ap;
+  va_start(ap, idx);
+  int r = ambus_call_simple_sync_va(ambus, intf, idx, ap);
+  va_end(ap);
+  return r;
+}
+
+int ambus_call_sync_general(struct aml_dbus *ambus, const char *destination, const char *path, const char *interface,
+                            const char *member, void *userdata, int (*msgpack)(sd_bus_message *m, void *userdata),
+                            int (*msgunpack)(sd_bus_message *m, void *userdata)) {
+  if (ambus_in_dispatch_thread(ambus)) {
+    AMBUS_LOGE("cannot make sync call in dispatch thread");
+    return -1;
+  }
+  int r = -1;
+  int done = 0;
+  int call_done(sd_bus_message * m, void *userdata, sd_bus_error *ret_error) {
+    if (r >= 0 && msgunpack)
+      r = msgunpack(m, userdata);
+    ambus_set_flag(ambus, &done, 1);
+    return 0;
+  }
+  void do_task(void *p) {
+    sd_bus_message *msg = NULL;
+    r = sd_bus_message_new_method_call(ambus->bus, &msg, destination, path, interface, member);
+    if (r >= 0 && msgpack)
+      r = msgpack(msg, userdata);
+    if (r >= 0)
+      r = sd_bus_call_async(ambus->bus, NULL, msg, call_done, userdata, 0);
+    else {
+      sd_bus_message_unref(msg);
+      ambus_set_flag(ambus, &done, 1);
+    }
+  }
+  if (ambus_post_task(ambus, do_task, NULL) == 0)
+    ambus_wait_flag(ambus, &done, 1);
+  return r;
+}
+
+int ambus_call_async_general(struct aml_dbus *ambus, const char *destination, const char *path, const char *interface,
+                             const char *member, void *userdata, int (*msgpack)(sd_bus_message *m, void *userdata),
+                             int (*reply_msg_handle)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)) {
+  int r = -1;
+  int done = 0;
+  void do_task(void *p) {
+    sd_bus_message *msg = NULL;
+    r = sd_bus_message_new_method_call(ambus->bus, &msg, destination, path, interface, member);
+    if (r >= 0 && msgpack)
+      r = msgpack(msg, userdata);
+    if (r >= 0)
+      r = sd_bus_call_async(ambus->bus, NULL, msg, reply_msg_handle, userdata, 0);
+    else {
+      sd_bus_message_unref(msg);
+    }
+    if (p)
+      ambus_set_flag(ambus, (uint32_t *)p, 1);
+  }
+  if (ambus_in_dispatch_thread(ambus)) {
+    do_task(NULL);
+  } else {
+    uint32_t done = 0;
+    if (ambus_post_task(ambus, do_task, &done) == 0)
+      ambus_wait_flag(ambus, &done, 1);
+  }
+  return r;
+}
+
+
+int ambus_run_in_dispatch(struct aml_dbus *ambus, void (*cb)(void *), void *param) {
+  if (ambus_in_dispatch_thread(ambus)) {
+    cb(param);
+  } else {
+    uint32_t done = 0;
+    void do_task(void *p) {
+      cb(param);
+      ambus_set_flag(ambus, &done, 1);
+    }
+    if (ambus_post_task(ambus, do_task, NULL) == 0)
+      ambus_wait_flag(ambus, &done, 1);
+  }
+  return 0;
+}
diff --git a/aml_dbus/aml-dbus.h b/aml_dbus/aml-dbus.h
new file mode 100644
index 0000000..e3f57ae
--- /dev/null
+++ b/aml_dbus/aml-dbus.h
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2014-2019 Amlogic, Inc. All rights reserved.
+ *
+ * All information contained herein is Amlogic confidential.
+ *
+ * This software is provided to you pursuant to Software License Agreement
+ * (SLA) with Amlogic Inc ("Amlogic"). This software may be used
+ * only in accordance with the terms of this agreement.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification is strictly prohibited without prior written permission from
+ * Amlogic.
+ *
+ * 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
+ * OWNER 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 <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
+
+#ifndef AML_DBUS_H
+#define AML_DBUS_H
+
+#define AMBUS_LOGE(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define AMBUS_LOGW(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define AMBUS_LOGI(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define AMBUS_LOGD(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define AMBUS_LOGV(fmt, ...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
+
+#define MACRO_CAT(a, b) a##b
+#define MACRO_CAT2(a, b) MACRO_CAT(a, b)
+#define MACRO_STRINGIFY(a) #a
+#define MACRO_STRINGIFY2(a) MACRO_STRINGIFY(a)
+#define MACRO_GET_8TH(_1,_2,_3,_4,_5,_6,_7,_8,...) _8
+#define MACRO_GET1ST(...) MACRO_GET_8TH(_1,_2,_3,_4,_5,_6,_7,__VA_ARGS__)
+#define MACRO_GET2ND(...) MACRO_GET_8TH(_1,_2,_3,_4,_5,_6,__VA_ARGS__)
+#define MACRO_GET3RD(...) MACRO_GET_8TH(_1,_2,_3,_4,_5,__VA_ARGS__)
+#define MACRO_GET4TH(...) MACRO_GET_8TH(_1,_2,_3,_4,__VA_ARGS__)
+#define MACRO_GET5TH(...) MACRO_GET_8TH(_1,_2,_3,__VA_ARGS__)
+#define MACRO_GET6TH(...) MACRO_GET_8TH(_1,_2,__VA_ARGS__)
+#define MACRO_GET7TH(...) MACRO_GET_8TH(_1,__VA_ARGS__)
+#define MACRO_GET8TH(...) MACRO_GET_8TH(__VA_ARGS__)
+#define MACRO_REMOVE_1ST_(a,...) __VA_ARGS__
+#define MACRO_REMOVE_1ST(...) MACRO_REMOVE_1ST_(__VA_ARGS__)
+#define MACRO_EMPTY(...)
+
+
+// https://github.com/swansontec/map-macro
+// modified to avoid name conflicts and add additional parameter
+#define MACRO_EVAL0(...) __VA_ARGS__
+#define MACRO_EVAL1(...) MACRO_EVAL0(MACRO_EVAL0(MACRO_EVAL0(__VA_ARGS__)))
+#define MACRO_EVAL2(...) MACRO_EVAL1(MACRO_EVAL1(MACRO_EVAL1(__VA_ARGS__)))
+#define MACRO_EVAL3(...) MACRO_EVAL2(MACRO_EVAL2(MACRO_EVAL2(__VA_ARGS__)))
+#define MACRO_EVAL4(...) MACRO_EVAL3(MACRO_EVAL3(MACRO_EVAL3(__VA_ARGS__)))
+#define MACRO_EVAL(...) MACRO_EVAL4(MACRO_EVAL4(MACRO_EVAL4(__VA_ARGS__)))
+
+#define MACRO_MAP_END(...)
+#define MACRO_MAP_OUT
+#define MACRO_MAP_COMMA ,
+
+#define MACRO_MAP_GET_END2() 0, MACRO_MAP_END
+#define MACRO_MAP_GET_END1(...) MACRO_MAP_GET_END2
+#define MACRO_MAP_GET_END(...) MACRO_MAP_GET_END1
+#define MACRO_MAP_NEXT0(test, next, ...) next MACRO_MAP_OUT
+#define MACRO_MAP_NEXT1(test, next) MACRO_MAP_NEXT0(test, next, 0)
+#define MACRO_MAP_NEXT(test, next) MACRO_MAP_NEXT1(MACRO_MAP_GET_END test, next)
+
+#define MACRO_MAP0(f, P, x, peek, ...) f(P, x) MACRO_MAP_NEXT(peek, MACRO_MAP1)(f, P, peek, __VA_ARGS__)
+#define MACRO_MAP1(f, P, x, peek, ...) f(P, x) MACRO_MAP_NEXT(peek, MACRO_MAP0)(f, P, peek, __VA_ARGS__)
+#define MACRO_MAP(f, P, ...) MACRO_EVAL(MACRO_MAP1(f, P, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define MACRO_MAP_PAIR0(f, P, x, y, peek, ...) MACRO_MAP_NEXT(x,f)(P, x, y) MACRO_MAP_NEXT(peek, MACRO_MAP_PAIR1)(f, P, peek, __VA_ARGS__)
+#define MACRO_MAP_PAIR1(f, P, x, y, peek, ...) MACRO_MAP_NEXT(x,f)(P, x, y) MACRO_MAP_NEXT(peek, MACRO_MAP_PAIR0)(f, P, peek, __VA_ARGS__)
+#define MACRO_MAP_PAIR(f, P, ...) MACRO_EVAL(MACRO_MAP_PAIR1(f, P, ##__VA_ARGS__, ()()(), ()()(), ()()()))
+
+#define AMBUS_CHECK(v, x, e)                                                                                           \
+  do {                                                                                                                 \
+    if ((v = (x)) < 0) {                                                                                               \
+      AMBUS_LOGW("call %s fail %d %s\n", #x, v, strerror(-v));                                                         \
+      e;                                                                                                               \
+    }                                                                                                                  \
+  } while (0)
+
+#define POST_AND_WAIT_DISPATCH(ambus, thread_expr)                                                                     \
+  do {                                                                                                                 \
+    uint32_t done = 0;                                                                                                 \
+    void do_task(void *p) {                                                                                            \
+      thread_expr;                                                                                                     \
+      ambus_set_flag(ambus, &done, 1);                                                                                 \
+    }                                                                                                                  \
+    if (ambus_post_task(ambus, do_task, NULL) == 0)                                                                    \
+      ambus_wait_flag(ambus, &done, 1);                                                                                \
+  } while (0)
+
+#define RUN_IN_DISPATCH_THREAD(ambus, thread_expr)                                                                     \
+  do {                                                                                                                 \
+    if (!ambus_in_dispatch_thread(ambus)) {                                                                            \
+      POST_AND_WAIT_DISPATCH(ambus, thread_expr);                                                                      \
+    } else {                                                                                                           \
+      thread_expr;                                                                                                     \
+    }                                                                                                                  \
+  } while (0)
+
+#define AMBUS_INTERFACE_PTR(_intf) (&_intf##_interface)
+#define AMBUS_VTABLE(_intf, _member) (AMBUS_INTERFACE_PTR(_intf)->vtable[AMBUS_MEMBER_IDX(_intf, _member)])
+#define AMBUS_SERVICE(_intf) AMBUS_INTERFACE_PTR(_intf)->service
+#define AMBUS_OBJECT(_intf) AMBUS_INTERFACE_PTR(_intf)->object
+#define AMBUS_INTERFACE(_intf) AMBUS_INTERFACE_PTR(_intf)->interface
+
+#define AMBUS_MEMBER_IDX(_intf, _member) _intf##_enum_##_member
+#define AMBUS_ADD_VTABLE(_ambus, _intf, _userData)                                                                     \
+  sd_bus_add_object_vtable(ambus_sdbus(_ambus), NULL, AMBUS_OBJECT(_intf), AMBUS_INTERFACE(_intf), _intf##_vtable,     \
+                           _userData)
+#define AMBUS_NEW_SIGNAL(_sig, _ambus, _intf, _member)                                                                 \
+  sd_bus_message_new_signal(ambus_sdbus(_ambus), _sig, AMBUS_OBJECT(_intf), AMBUS_INTERFACE(_intf), _member)
+#define AMBUS_REQUEST_NAME(_ambus, _intf) sd_bus_request_name(ambus_sdbus(_ambus), AMBUS_SERVICE(_intf), 0)
+
+#define GEN_ENUM_ITEM(p, _member, ...) AMBUS_MEMBER_IDX(p, _member),
+#define GEN_EMPTY(...)
+#define GEN_VTABLE2(p, _member, _signature) {#_member, _signature, NULL},
+#define GEN_VTABLE3(p, _member, _signature, _result) {#_member, _signature, _result},
+#define GEN_PROP_DECL(p, _member, _signature)                                                                          \
+  static int p##_property_get(sd_bus *bus, const char *path, const char *interface, const char *property,              \
+                              sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
+#define GEN_PROPRW_DECL(p, _member, _signature)                                                                        \
+  static int p##_property_get(sd_bus *bus, const char *path, const char *interface, const char *property,              \
+                              sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);                         \
+  static int p##_property_set(sd_bus *bus, const char *path, const char *interface, const char *property,              \
+                              sd_bus_message *value, void *userdata, sd_bus_error *ret_error);
+#define GEN_METHOD_DECL(p, _member, _signature, _result)                                                               \
+  static int p##_method_##_member(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
+
+#define GEN_VTABLE_PROP(p, _member, _signature) SD_BUS_PROPERTY(#_member, _signature, p##_property_get, 0, 0),
+#define GEN_VTABLE_PROPRW(p, _member, _signature)                                                                      \
+  SD_BUS_WRITABLE_PROPERTY(#_member, _signature, p##_property_get, p##_property_set, 0, 0),
+#define GEN_VTABLE_METHOD(p, _member, _signature, _result)                                                             \
+  SD_BUS_METHOD(#_member, _signature, _result, p##_method_##_member, SD_BUS_VTABLE_UNPRIVILEGED),
+#define GEN_VTABLE_SIGNAL(p, _member, _signature) SD_BUS_SIGNAL(#_member, _signature, 0),
+
+#define AMBUS_DECLARE_INTERFACE(M)                                                                                     \
+  enum { M(M, GEN_ENUM_ITEM, GEN_ENUM_ITEM, GEN_ENUM_ITEM, GEN_ENUM_ITEM) M##_enum_END_ };                             \
+  extern struct ambus_interface M##_interface;
+
+#define AMBUS_DEFINE_INTERFACE(M, service, object, interface)                                                          \
+  struct ambus_interface M##_interface = {                                                                             \
+      service, object, interface, {M(M, GEN_VTABLE2, GEN_VTABLE2, GEN_VTABLE3, GEN_VTABLE2){0, 0, 0}}};
+
+#define AMBUS_DECLARE_VTABLE(M) M(M, GEN_PROP_DECL, GEN_PROPRW_DECL, GEN_METHOD_DECL, GEN_EMPTY)
+#define AMBUS_DEFINE_VTABLE(M)                                                                                         \
+  static const sd_bus_vtable M##_vtable[] = {                                                                          \
+      SD_BUS_VTABLE_START(0),                                                                                          \
+      M(M, GEN_VTABLE_PROP, GEN_VTABLE_PROPRW, GEN_VTABLE_METHOD, GEN_VTABLE_SIGNAL) SD_BUS_VTABLE_END};
+
+
+#define AMBUS_DATA_TYPE_uint8_t "y", uint8_t, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_bool "b", int, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_int16_t "n", int16_t, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_uint16_t "q", uint16_t, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_int32_t "i", int32_t,ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_uint32_t "u", uint32_t,ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_int64_t "x", int64_t, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_uint64_t "t", uint64_t, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_double "d", double, ambus_data_pack_basic, ambus_data_unpack_basic
+#define AMBUS_DATA_TYPE_string "s", char *,ambus_data_pack_basic, ambus_data_unpack_string
+
+#define AMBUS_DATA_TYPE_SIG(x) MACRO_GET1ST(MACRO_CAT2(AMBUS_DATA_TYPE_,x))
+#define AMBUS_DATA_TYPE_CT(x) MACRO_GET2ND(MACRO_CAT2(AMBUS_DATA_TYPE_,x))
+#define AMBUS_DATA_TYPE_PACK(x) MACRO_GET3RD(MACRO_CAT2(AMBUS_DATA_TYPE_,x))
+#define AMBUS_DATA_TYPE_UNPACK(x) MACRO_GET4TH(MACRO_CAT2(AMBUS_DATA_TYPE_,x))
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aml_dbus;
+
+struct ambus_vtable {
+  const char *member;
+  const char *signature;
+  const char *result;
+};
+
+struct ambus_interface {
+  const char *service;
+  const char *object;
+  const char *interface;
+  struct ambus_vtable vtable[];
+};
+
+static const char *AMBUS_DEFAULT_SYSTEM = (const char *)(0);
+static const char *AMBUS_DEFAULT_USER = (const char *)(1);
+static const char *AMBUS_DEFAULT_NONE = (const char *)(2);
+/**
+ * @brief create a new dbus connection, this function MUST be called in dispatch thread (before ambus_run)
+ *
+ * @param address, dbus address, special addresses:
+ *   NULL/AMBUS_DEFAULT_SYSTEM: default system bus
+ *   AMBUS_DEFAULT_USER: default session bus
+ *   AMBUS_DEFAULT_NONE: do not open dbus connection, only create event loop
+ * @param mode, resolved, must be 0 by now
+ *
+ * @return return a pointer of opaque aml_dbus object
+ */
+struct aml_dbus *ambus_new(const char *address, int mode);
+
+/**
+ * @brief ensure dbus is created and dispatch thread is running
+ *
+ * @param ppambus, specify pointer of the bus to be ensured, and output the new created bus
+ *   if NULL, it will always create new bus and dispatch thread
+ *
+ * @return return new created ambus if not created, else return *ppambus
+ */
+struct aml_dbus *ambus_ensure_run(struct aml_dbus **ppambus);
+
+/**
+ * @brief free the bus object
+ *
+ * @param ambus
+ */
+void ambus_free(struct aml_dbus *ambus);
+
+/**
+ * @brief run the event loop, dispatch dbus messages, must be called in the same thread with ambus_new
+ *
+ * @param ambus
+ *
+ * @return
+ */
+int ambus_run(struct aml_dbus *ambus);
+sd_bus *ambus_sdbus(struct aml_dbus *ambus);
+sd_event *ambus_sdevent(struct aml_dbus *ambus);
+// check whether current thread is dispatch thread
+bool ambus_in_dispatch_thread(struct aml_dbus *ambus);
+// wait until (*val & mask) != 0
+void ambus_wait_flag(struct aml_dbus *ambus, uint32_t *val, uint32_t mask);
+// set flag *val |= mask and notify all waiters
+void ambus_set_flag(struct aml_dbus *ambus, uint32_t *val, uint32_t mask);
+// call cb(param) in dispatch thread asynchronously
+int ambus_post_task(struct aml_dbus *ambus, void (*cb)(void *), void *param);
+// call cb(param) in dispatch thread synchronously
+int ambus_run_in_dispatch(struct aml_dbus *ambus, void (*cb)(void *), void *param);
+// call cb(param) after delay_ms in dispatch thread asynchronously
+int ambus_post_delay_task(struct aml_dbus *ambus, uint32_t delay_ms, void (*cb)(void *), void *param);
+// call dbus method asynchronously, msgpack is used to pack input arguments of the method
+int ambus_call_async_general(struct aml_dbus *ambus, const char *destination, const char *path, const char *interface,
+                             const char *member, void *userdata, int (*msgpack)(sd_bus_message *m, void *userdata),
+                             int (*reply_msg_handle)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error));
+// call dbus method synchronously, it MUST be call in a thread other than dispatch thread
+int ambus_call_sync_general(struct aml_dbus *ambus, const char *destination, const char *path, const char *interface,
+                            const char *member, void *userdata, int (*msgpack)(sd_bus_message *m, void *userdata),
+                            int (*msgunpack)(sd_bus_message *m, void *userdata));
+
+#ifdef __cplusplus
+} /* extern "C" */
+#include <iostream>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include <functional>
+#include <map>
+#include <unordered_map>
+
+// T shall be decay type, no const, no reference, no pointer
+template <typename T, typename TEMPTY = void> struct ambus_data_type;
+
+template <char... sigs> struct ambus_signature {
+  constexpr static char value[] = {sigs..., 0};
+};
+template <char... sigs> constexpr char ambus_signature<sigs...>::value[];
+template <typename... Args> struct ambus_signature_concat;
+template <char... sig1, char... sig2>
+struct ambus_signature_concat<ambus_signature<sig1...>, ambus_signature<sig2...>> {
+  using type = ambus_signature<sig1..., sig2...>;
+};
+template <typename T1, typename... Args> struct ambus_signature_concat<T1, Args...> {
+  using type = typename ambus_signature_concat<T1, typename ambus_signature_concat<Args...>::type>::type;
+};
+
+template <typename T, int sig, typename CT = T> struct ambus_simple_type {
+  using signature = ambus_signature<sig>;
+  static int pack(sd_bus_message *msg, const T &val) {
+    const CT ct = static_cast<CT>(val);
+    return sd_bus_message_append_basic(msg, sig, &ct);
+  }
+  static int unpack(sd_bus_message *msg, T &val) {
+    CT ct;
+    int r = sd_bus_message_read_basic(msg, sig, &ct);
+    if (r > 0)
+      val = static_cast<T>(ct);
+    return r;
+  }
+};
+
+template <typename T, int S = sizeof(T)> struct ambus_simple_struct {
+  using signature = ambus_signature<'a', 'y'>;
+  static int pack(sd_bus_message *msg, const T &val) {
+    return sd_bus_message_append_array(msg, 'y', (const void *)&val, S);
+  }
+  static int unpack(sd_bus_message *msg, T &val) {
+    const void *ptr;
+    size_t size = 0;
+    int r = sd_bus_message_read_array(msg, 'y', &ptr, &size);
+    if (r >= 0 && size == S)
+      memcpy(&val, ptr, size);
+    else
+      AMBUS_LOGW("fail to read array from msg, ret %d size %d sizeof(T) %d", r, (int)size, S);
+    return r;
+  }
+};
+template <typename T, int N> struct ambus_data_type<T[N]> : ambus_simple_struct<T[N]> {};
+
+// buildin types
+template <> struct ambus_data_type<char> : ambus_simple_type<char, 'y'> {};
+template <> struct ambus_data_type<uint8_t> : ambus_simple_type<uint8_t, 'y'> {};
+template <> struct ambus_data_type<int8_t> : ambus_simple_type<int8_t, 'y'> {};
+template <> struct ambus_data_type<bool> : ambus_simple_type<bool, 'b', int> {};
+template <> struct ambus_data_type<int16_t> : ambus_simple_type<int16_t, 'n'> {};
+template <> struct ambus_data_type<uint16_t> : ambus_simple_type<uint16_t, 'q'> {};
+template <> struct ambus_data_type<int32_t> : ambus_simple_type<int32_t, 'i'> {};
+template <> struct ambus_data_type<uint32_t> : ambus_simple_type<uint32_t, 'u'> {};
+template <> struct ambus_data_type<int64_t> : ambus_simple_type<int64_t, 'x'> {};
+template <> struct ambus_data_type<uint64_t> : ambus_simple_type<uint64_t, 't'> {};
+template <> struct ambus_data_type<double> : ambus_simple_type<double, 'd'> {};
+template <> struct ambus_data_type<long> : ambus_simple_type<long, 'x', int64_t> {};
+//template <> struct ambus_data_type<const char *> : ambus_simple_type<const char *, 's'> {};
+template <typename T>
+struct ambus_data_type<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
+    : ambus_simple_type<T, 'd', double> {};
+template <typename T>
+struct ambus_data_type<T, typename std::enable_if<std::is_enum<T>::value>::type> : ambus_simple_type<T, 'i', int32_t> {
+};
+template <typename T>
+struct ambus_data_type<T, typename std::enable_if<std::is_pod<T>::value && std::is_class<T>::value>::type>
+    : ambus_simple_struct<T> {};
+template <> struct ambus_data_type<std::string> {
+  using signature = ambus_signature<'s'>;
+  static int pack(sd_bus_message *msg, const std::string &val) {
+    return sd_bus_message_append_basic(msg, 's', val.c_str());
+  }
+  static int unpack(sd_bus_message *msg, std::string &val) {
+    const char *ptr = NULL;
+    int r = sd_bus_message_read_basic(msg, 's', &ptr);
+    if (r >= 0 && ptr)
+      val = ptr;
+    return r;
+  }
+};
+template <template <class...> class C, typename T> struct ambus_data_type<C<T>> {
+  using signature =
+      typename ambus_signature_concat<ambus_signature<'a'>,
+                                      typename ambus_data_type<typename std::decay<T>::type>::signature>::type;
+  static int pack(sd_bus_message *msg, const C<T> &val) {
+    int r = sd_bus_message_open_container(msg, 'a', ambus_data_type<typename std::decay<T>::type>::signature::value);
+    for (auto it = val.begin(); r >= 0 && it != val.end(); ++it)
+      r = ambus_data_type<typename std::decay<T>::type>::pack(msg, *it);
+    sd_bus_message_close_container(msg);
+    return 0;
+  }
+  static int unpack(sd_bus_message *msg, C<T> &val) {
+    int r = sd_bus_message_enter_container(msg, 'a', ambus_data_type<typename std::decay<T>::type>::signature::value);
+    T item;
+    while ((r = ambus_data_type<typename std::decay<T>::type>::unpack(msg, item)) > 0)
+      val.push_back(item);
+    sd_bus_message_exit_container(msg);
+    return 0;
+  }
+};
+
+template <template <class...> class M, typename K, typename V>
+struct ambus_data_type<M<K, V>, typename std::enable_if<std::is_same<M<K, V>, std::map<K, V>>::value ||
+                                                        std::is_same<M<K, V>, std::unordered_map<K, V>>::value>::type> {
+  using decay_val_type = typename std::decay<V>::type;
+  using signature_kv = typename ambus_signature_concat<typename ambus_data_type<K>::signature,
+                                                       typename ambus_data_type<decay_val_type>::signature>::type;
+  using signature_map = typename ambus_signature_concat<ambus_signature<'{'>, signature_kv, ambus_signature<'}'>>::type;
+  using signature = typename ambus_signature_concat<ambus_signature<'a'>, signature_map>::type;
+  static int pack(sd_bus_message *msg, const M<K, V> &val) {
+    int r = sd_bus_message_open_container(msg, 'a', signature_map::value);
+    for (auto it = val.begin(); r >= 0 && it != val.end(); ++it) {
+      if ((r = sd_bus_message_open_container(msg, 'e', signature_kv::value)) >= 0) {
+        if ((r = ambus_data_type<K>::pack(msg, it->first)) >= 0)
+          r = ambus_data_type<decay_val_type>::pack(msg, it->second);
+        sd_bus_message_close_container(msg);
+      }
+    }
+    sd_bus_message_close_container(msg);
+    return 0;
+  }
+  static int unpack(sd_bus_message *msg, M<K, V> &val) {
+    int r = sd_bus_message_enter_container(msg, 'a', signature_map::value);
+    for (; (r = sd_bus_message_enter_container(msg, 'e', signature_kv::value)) > 0;
+         sd_bus_message_exit_container(msg)) {
+      K k;
+      if ((r = ambus_data_type<K>::unpack(msg, k)) > 0)
+        r = ambus_data_type<decay_val_type>::unpack(msg, val[k]);
+    }
+    sd_bus_message_exit_container(msg);
+    return r;
+  }
+};
+
+// output argument must be non-const reference
+template <typename T>
+struct is_output_parameter
+    : public std::integral_constant<bool, std::is_lvalue_reference<T>::value &&
+                                              !std::is_const<typename std::remove_reference<T>::type>::value> {};
+
+template <typename... Args> struct ambus_data_pack;
+
+template <typename T, typename... Args> struct ambus_data_pack<T, Args...> : public ambus_data_pack<Args...> {
+  constexpr static bool is_output =
+      std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
+  using parent_class = ambus_data_pack<Args...>;
+  using decay_value_type = typename std::conditional<std::is_array<T>::value, T, typename std::decay<T>::type>::type;
+  using signature_output = typename ambus_signature_concat<
+      typename std::conditional<is_output, typename ambus_data_type<decay_value_type>::signature,
+                                ambus_signature<>>::type,
+      typename parent_class::signature_output>::type;
+  using signature_input = typename ambus_signature_concat<
+      typename std::conditional<!is_output, typename ambus_data_type<decay_value_type>::signature,
+                                ambus_signature<>>::type,
+      typename parent_class::signature_input>::type;
+
+  static int packall(bool output, sd_bus_message *msg, const decay_value_type &val1, const Args &... vals) {
+    int ret = is_output == output ? ambus_data_type<decay_value_type>::pack(msg, val1) : 0;
+    return ret >= 0 ? parent_class::packall(output, msg, vals...) : ret;
+  }
+  static int unpackall(bool output, sd_bus_message *msg, T &val1, Args &... vals) {
+    int ret = is_output == output ? ambus_data_type<decay_value_type>::unpack(msg, const_cast<decay_value_type&>(val1)) : 1;
+    return ret > 0 ? parent_class::unpackall(output, msg, vals...) : ret;
+  }
+  decay_value_type val;
+  int unpack(bool output, sd_bus_message *msg) {
+    int ret = output == is_output_parameter<T>::value ? ambus_data_type<decay_value_type>::unpack(msg, val) : 0;
+    return ret >= 0 ? parent_class::unpack(output, msg) : ret;
+  }
+  int pack(bool output, sd_bus_message *msg) {
+    int ret = output == is_output_parameter<T>::value ? ambus_data_type<decay_value_type>::pack(msg, val) : 0;
+    return ret >= 0 ? parent_class::pack(output, msg) : ret;
+  }
+  template <typename RET, typename... FArgs, typename... CArgs>
+  int apply(RET &ret, RET (*pfn)(FArgs...), CArgs &... args) {
+    return parent_class::apply(ret, pfn, args..., val);
+  }
+  template <typename TOBJ, typename RET, typename... FArgs, typename... CArgs>
+  int apply_member(TOBJ *pThis, RET &ret, RET (TOBJ::*pfn)(FArgs...), CArgs &... args) {
+    return parent_class::apply_member(pThis, ret, pfn, args..., val);
+  }
+};
+
+template <> struct ambus_data_pack<> {
+  // virtual ~ambus_data_pack<>() {}
+  using signature_output = ambus_signature<>;
+  using signature_input = ambus_signature<>;
+  static int packall(bool output, sd_bus_message *msg) { return 0; }
+  static int unpackall(bool output, sd_bus_message *msg) { return 1; }
+  int unpack(bool output, sd_bus_message *msg) { return 0; }
+  int pack(bool output, sd_bus_message *msg) { return 0; }
+  template <typename RET, typename... FArgs, typename... CArgs>
+  int apply(RET &ret, RET (*pfn)(FArgs...), CArgs &... args) {
+    ret = pfn(args...);
+    return 0;
+  }
+  template <typename TOBJ, typename RET, typename... FArgs, typename... CArgs>
+  int apply_member(TOBJ *pThis, RET &ret, RET (TOBJ::*pfn)(FArgs...), CArgs &... args) {
+    ret = (pThis->*pfn)(args...);
+    return 0;
+  }
+};
+
+#define GEN_STRUCT_FIELDS_TYPE(T, F) , decltype(T::F)
+#define GEN_STRUCT_FIELDS_VAL(v, F) , v.F
+#define GEN_STRUCT_FIELDS_UNPACK(T, F)                                                                                 \
+  if (r >= 0)                                                                                                          \
+    r = ambus_data_type<decltype(T::F)>::unpack(msg, val.F);
+
+#define AMBUS_STRUCT_DEFINE(T, F1, ...)                                                                                \
+  template <> struct ambus_data_type<T> {                                                                              \
+    using fields_pack = ambus_data_pack<decltype(T::F1) MACRO_MAP(GEN_STRUCT_FIELDS_TYPE, T, __VA_ARGS__)>;            \
+    using signature = typename ambus_signature_concat<ambus_signature<'('>, fields_pack::signature_input,              \
+                                                      ambus_signature<')'>>::type;                                     \
+    static int pack(sd_bus_message *msg, const T &val) {                                                               \
+      int r = sd_bus_message_open_container(msg, 'r', fields_pack::signature_input::value);                            \
+      if (r >= 0)                                                                                                      \
+        r = fields_pack::packall(false, msg, val.F1 MACRO_MAP(GEN_STRUCT_FIELDS_VAL, val, __VA_ARGS__));               \
+      sd_bus_message_close_container(msg);                                                                             \
+      return r;                                                                                                        \
+    }                                                                                                                  \
+    static int unpack(sd_bus_message *msg, T &val) {                                                                   \
+      int r = sd_bus_message_enter_container(msg, 'r', fields_pack::signature_input::value);                           \
+      if (r >= 0)                                                                                                      \
+        r = fields_pack::unpackall(false, msg, val.F1 MACRO_MAP(GEN_STRUCT_FIELDS_VAL, val, __VA_ARGS__));             \
+      sd_bus_message_exit_container(msg);                                                                              \
+      return r;                                                                                                        \
+    }                                                                                                                  \
+  }
+
+template <typename... Args> struct ambus_method_call;
+template <> struct ambus_method_call<> : public ambus_vtable {
+  ambus_method_call<>(const char *_name, const char *_sig, const char *_res) {
+    member = _name;
+    signature = _sig;
+    result = _res;
+  }
+  virtual int call(sd_bus_message *m) { return 0; };
+};
+template <typename RET, typename CLS, typename... Args>
+struct ambus_method_call<RET (CLS::*)(Args...)> : public ambus_method_call<> {
+  using data_pack = ambus_data_pack<Args...>;
+  using signature_output = typename ambus_signature_concat<typename ambus_data_type<RET>::signature,
+                                                           typename data_pack::signature_output>::type;
+  ambus_method_call<RET (CLS::*)(Args...)>(CLS *_this, const char *name, RET (CLS::*pfn)(Args...))
+      : pThis(_this),
+        callback(pfn), ambus_method_call<>(name, data_pack::signature_input::value, signature_output::value) {}
+  virtual int call(sd_bus_message *m) {
+    data_pack val;
+    int r = 0;
+    if (signature[0])
+      r = val.unpack(false, m);
+    RET ret;
+    if (r >= 0)
+      r = val.apply_member(pThis, ret, callback);
+    sd_bus_message *reply = NULL;
+    if (r >= 0)
+      r = sd_bus_message_new_method_return(m, &reply);
+    if (r >= 0)
+      r = ambus_data_type<RET>::pack(reply, ret);
+    if (r >= 0 && result[0])
+      r = val.pack(true, reply);
+    if (r >= 0)
+      r = sd_bus_send(sd_bus_message_get_bus(m), reply, NULL);
+    else if (reply)
+      sd_bus_message_unref(reply);
+    return r;
+  }
+  RET (CLS::*callback)(Args...);
+  CLS *pThis;
+};
+
+template <typename RET, typename... Args> struct ambus_method_call<RET (*)(Args...)> : public ambus_method_call<> {
+  using data_pack = ambus_data_pack<Args...>;
+  using signature_output = typename ambus_signature_concat<typename ambus_data_type<RET>::signature,
+                                                           typename data_pack::signature_output>::type;
+  ambus_method_call<RET (*)(Args...)>(const char *name, RET (*pfn)(Args...))
+      : callback(pfn), ambus_method_call<>(name, data_pack::signature_input::value, signature_output::value) {}
+  virtual int call(sd_bus_message *m) {
+    data_pack val;
+    int r = 0;
+    if (signature[0])
+      r = val.unpack(false, m);
+    RET ret;
+    if (r >= 0)
+      r = val.apply(ret, callback);
+    sd_bus_message *reply = NULL;
+    if (r >= 0)
+      r = sd_bus_message_new_method_return(m, &reply);
+    if (r >= 0)
+      r = ambus_data_type<RET>::pack(reply, ret);
+    if (r >= 0 && result[0])
+      r = val.pack(true, reply);
+    if (r >= 0)
+      r = sd_bus_send(sd_bus_message_get_bus(m), reply, NULL);
+    else if (reply)
+      sd_bus_message_unref(reply);
+    return r;
+  }
+  RET (*callback)(Args...);
+};
+
+struct ambus_runnable_task {
+  using prototype = std::function<void(void)>;
+  prototype func;
+  ambus_runnable_task(prototype &&f) : func(f){};
+  static void cb(void *p) { static_cast<ambus_runnable_task *>(p)->func(); };
+  int run_on(struct aml_dbus *ambus) { return ambus_run_in_dispatch(ambus, ambus_runnable_task::cb, this); }
+};
+
+template <typename T> struct ambus_method_proxy;
+struct ambus_method_proxy_packer {
+  std::function<int(sd_bus_message *)> pk, upk;
+  static int pack(sd_bus_message *m, void *userdata) { return ((ambus_method_proxy_packer *)userdata)->pk(m); }
+  static int unpack(sd_bus_message *m, void *userdata) { return ((ambus_method_proxy_packer *)userdata)->upk(m); }
+};
+template <typename RET, typename... Args> struct ambus_method_proxy<RET (*)(Args...)> {
+  static RET call(struct aml_dbus *ambus, const char *service, const char *object, const char *interface,
+                  const char *member, Args... args) {
+    RET ret;
+    ambus_method_proxy_packer p = {
+        [&](sd_bus_message *msg) { return ambus_data_pack<Args...>::packall(false, msg, args...); },
+        [&](sd_bus_message *msg) { return ambus_data_pack<RET &, Args...>::unpackall(true, msg, ret, args...); }};
+    int r = ambus_call_sync_general(ambus, service, object, interface, member, &p, ambus_method_proxy_packer::pack,
+                                    ambus_method_proxy_packer::unpack);
+    return ret;
+  }
+};
+
+#endif
+
+#endif /* end of include guard: AML_DBUS_H */
diff --git a/aml_dbus/sample-client.c b/aml_dbus/sample-client.c
new file mode 100644
index 0000000..0c0d3bb
--- /dev/null
+++ b/aml_dbus/sample-client.c
@@ -0,0 +1,92 @@
+#include "aml-dbus.h"
+
+
+#define TEST_SERVICE "amlogic.yocto.test"
+#define TEST_OBJECT "/amlogic/yocto/test/obj1"
+#define TEST_INTERFACE "amlogic.yocto.test.intf1"
+#define TEST_INTF(p, PROP, RROPRW, METHOD, SIGNAL)                                                                     \
+  PROP(p, Count, "i")                                                                                                  \
+  METHOD(p, TestBasicType, "ybnqiuxtds", "ybnqiuxtds")                                                                 \
+  METHOD(p, TestArray, "a(is)", "a(si)")                                                                               \
+  SIGNAL(p, Changed, "i")
+
+AMBUS_DECLARE_INTERFACE(TEST_INTF);
+AMBUS_DEFINE_INTERFACE(TEST_INTF, TEST_SERVICE, TEST_OBJECT, TEST_INTERFACE);
+
+#define AMBUS_SERV_OBJ_INTF(_intf) AMBUS_SERVICE(_intf), AMBUS_OBJECT(_intf), AMBUS_INTERFACE(_intf)
+#define AMBUS_SERV_OBJ_INTF_MEM(_intf, _mem) AMBUS_SERV_OBJ_INTF(_intf), #_mem
+
+// we use two bus, one for method, one for signal
+static struct aml_dbus *bus_method;
+static struct aml_dbus *bus_signal;
+
+static int dbus_signal_handle(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+  const char *mem = sd_bus_message_get_member(message);
+  if (strcmp(mem, "Changed") == 0) {
+    int v;
+    sd_bus_message_read_basic(message, 'i', &v);
+    //printf("got signal Changed, %d\n", v);
+  }
+  return 0;
+}
+
+#define MACRO_GET_2ND_(a, b, ...) b
+#define MACRO_GET_2ND(...) MACRO_GET_2ND_(__VA_ARGS__)
+#define AMBUS_DATA_PACK_BASIC(msg, type, ptr) sd_bus_message_append_basic(msg, type, ptr)
+#define AMBUS_DATA_UNPACK_BASIC(msg, type, ptr) sd_bus_message_read_basic(msg, type, ptr)
+#define AMBUS_DATA_TYPE_y uint8_t, AMBUS_DATA_PACK_BASIC, AMBUS_DATA_UNPACK_BASIC
+
+static int call_TestBasicType(uint8_t y, int b, int16_t n, uint16_t q, int32_t i, uint32_t u, int64_t x, uint64_t t,
+                              double d, char *s, uint8_t *oy, int *ob, int16_t *on, uint16_t *oq, int32_t *oi,
+                              uint32_t *ou, int64_t *ox, uint64_t *ot, double *od, char **os) {
+  struct ambus_vtable *mem = &AMBUS_VTABLE(TEST_INTF, TestBasicType);
+  int r = 0;
+  int pack(sd_bus_message * m, void *userdata) {
+    r = sd_bus_message_append(m, mem->signature, y, b, n, q, i, u, x, t, d, s);
+    return r;
+  }
+  int unpack(sd_bus_message * m, void *userdata) {
+    char *ts = NULL;
+    r = sd_bus_message_read(m, mem->result, oy, ob, on, oq, oi, ou, ox, ot, od, &ts);
+    if (r >= 0 && ts && os) // string type memory is in sd_bus_message, which becomes invalid after this function return
+      *os = strdup(ts);
+    return r;
+  }
+  return ambus_call_sync_general(bus_method, AMBUS_SERV_OBJ_INTF_MEM(TEST_INTF, TestBasicType), NULL, pack, unpack);
+}
+
+static int test_dbus_call() {
+  uint8_t y = 1;
+  int b = false;
+  int16_t n = 2;
+  uint16_t q = 3;
+  int32_t i = 4;
+  uint32_t u = 5;
+  int64_t x = 6;
+  uint64_t t = 7;
+  double d = 8.123;
+  char buf[32];
+  char *s = buf;
+  for (int ii = 0; ii < 3; ii++) {
+    sprintf(s = buf, "test%d", i);
+    int r = call_TestBasicType(y, b, n, q, i, u, x, t, d, s, &y, &b, &n, &q, &i, &u, &x, &t, &d, &s);
+    printf("call TestBasicType return y:%d b:%d n:%d q:%d i:%d u:%u x:%" PRId64 " t:%" PRIu64 " d:%f s:%s\n", y, b, n,
+           q, i, u, x, t, d, s);
+    free(s);
+  }
+}
+
+int main(int argc, char *argv[]) {
+  bus_method = ambus_ensure_run(NULL);
+  bus_signal = ambus_ensure_run(NULL);
+
+  char match[512];
+  snprintf(match, sizeof(match), "type='signal',sender='%s',path='%s',interface='%s'", AMBUS_SERV_OBJ_INTF(TEST_INTF));
+  int r = 0;
+  void installcb(void *param) { r = sd_bus_add_match(ambus_sdbus(bus_signal), NULL, match, dbus_signal_handle, NULL); }
+  // make sd_bus_add_match be called in bus_signal dispatch thread
+  ambus_run_in_dispatch(bus_signal, installcb, NULL);
+  test_dbus_call();
+  ambus_run(bus_signal);
+  return 0;
+}
diff --git a/aml_dbus/sample-server.c b/aml_dbus/sample-server.c
new file mode 100644
index 0000000..dadffcd
--- /dev/null
+++ b/aml_dbus/sample-server.c
@@ -0,0 +1,135 @@
+#include "aml-dbus.h"
+#include <errno.h>
+
+#define TEST_SERVICE "amlogic.yocto.test"
+#define TEST_OBJECT "/amlogic/yocto/test/obj1"
+#define TEST_INTERFACE "amlogic.yocto.test.intf1"
+#define TEST_INTF(p, PROP, RROPRW, METHOD, SIGNAL)                                                                     \
+  PROP(p, Count, "i")                                                                                                  \
+  METHOD(p, TestBasicType, "ybnqiuxtds", "ybnqiuxtds")                                                                 \
+  METHOD(p, TestArray, "a(is)", "a(si)")                                                                               \
+  SIGNAL(p, Changed, "i")
+
+AMBUS_DECLARE_INTERFACE(TEST_INTF);
+//AMBUS_DEFINE_INTERFACE(TEST_INTF, TEST_SERVICE, TEST_OBJECT, TEST_INTERFACE);
+AMBUS_DEFINE_INTERFACE(TEST_INTF, NULL, NULL, NULL);
+
+#define AMBUS_SERV_OBJ_INTF(_intf) AMBUS_SERVICE(_intf), AMBUS_OBJECT(_intf), AMBUS_INTERFACE(_intf)
+
+struct TestObject {
+  int count;
+};
+
+static int TEST_INTF_property_get(sd_bus *bus, const char *path, const char *interface, const char *property,
+                                  sd_bus_message *reply, void *userdata, sd_bus_error *ret_error) {
+  struct TestObject *o = (struct TestObject *)userdata;
+  if (strcmp(property, "Count") == 0) {
+    return sd_bus_message_append(reply, "i", o->count);
+  }
+  return -EINVAL;
+}
+
+static int TEST_INTF_method_TestBasicType(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+  struct TestObject *o = (struct TestObject *)userdata;
+  uint8_t y;
+  int b;
+  int16_t n;
+  uint16_t q;
+  int32_t i;
+  uint32_t u;
+  int64_t x;
+  uint64_t t;
+  double d;
+  char *s;
+  int r = sd_bus_message_read(m, "ybnqiuxtds", &y, &b, &n, &q, &i, &u, &x, &t, &d, &s);
+  o->count++;
+  sd_bus_emit_signal(sd_bus_message_get_bus(m), sd_bus_message_get_path(m), sd_bus_message_get_interface(m), "Changed",
+                     "i", o->count);
+  y++;
+  b=!b;
+  n++;
+  q++;
+  i++;
+  u++;
+  x++;
+  t++;
+  d+=1;
+  s+=1;
+  return sd_bus_reply_method_return(m,"ybnqiuxtds",y, b, n, q, i, u, x, t, d, s);
+}
+
+static int TEST_INTF_method_TestArray(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+  struct TestObject *o = (struct TestObject *)userdata;
+  int r;
+  int i;
+  char *s;
+  sd_bus_message *reply;
+  AMBUS_CHECK(r, sd_bus_message_enter_container(m, 'a', "(is)"),);
+  AMBUS_CHECK(r, sd_bus_message_new_method_return(m, &reply),);
+  AMBUS_CHECK(r, sd_bus_message_open_container(reply, 'a', "(si)"),);
+  while ((r = sd_bus_message_read(m, "(is)", &i, &s)) > 0) {
+    AMBUS_CHECK(r,sd_bus_message_append(reply, "(si)", s, i),);
+  }
+  sd_bus_message_exit_container(m);
+  sd_bus_message_close_container(reply);
+  return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL);
+}
+
+AMBUS_DEFINE_VTABLE(TEST_INTF);
+
+static int call_done_TestBasicType(sd_bus_message *m, void *userdata, sd_bus_error *ret_error){
+  uint8_t y;
+  int b;
+  int16_t n;
+  uint16_t q;
+  int32_t i;
+  uint32_t u;
+  int64_t x;
+  uint64_t t;
+  double d;
+  char *s;
+  struct ambus_vtable *mem = &AMBUS_VTABLE(TEST_INTF, TestBasicType);
+  int r = sd_bus_message_read(m, mem->result, &y, &b, &n, &q, &i, &u, &x, &t, &d, &s);
+  printf("call TestBasicType return y:%d b:%d n:%d q:%d i:%d u:%u x:%" PRId64 " t:%" PRIu64 " d:%f s:%s\n", y, b, n, q,
+         i, u, x, t, d, s);
+  // the memory of 's' string is point to somewhere in sd_bus_message, is is invalid when sd_bus_message is unrefed
+  // you can sd_bus_message_ref to keep the memory of sd_bus_message, but don't forget to sd_bus_message_unref
+  return 0;
+}
+
+static int test_call_async(struct aml_dbus *ambus) {
+  struct ambus_vtable *mem = &AMBUS_VTABLE(TEST_INTF, TestBasicType);
+  int r = 0;
+  uint8_t y = 1;
+  int b = false;
+  int16_t n = 2;
+  uint16_t q = 3;
+  int32_t i = 4;
+  uint32_t u = 5;
+  int64_t x = 6;
+  uint64_t t = 7;
+  double d = 8.123;
+  char *s = "aaaaa";
+  int pack(sd_bus_message * m, void *userdata) {
+    return sd_bus_message_append(m, mem->signature, y, b, n, q, i, u, x, t, d, s);
+  }
+  ambus_call_async_general(ambus, AMBUS_SERV_OBJ_INTF(TEST_INTF), "TestBasicType", NULL, pack, call_done_TestBasicType);
+}
+
+int main(int argc, char *argv[])
+{
+  struct TestObject obj;
+  memset(&obj, 0, sizeof(obj));
+  int r = 0;
+  struct aml_dbus *ambus = ambus_new(NULL, 0);
+  AMBUS_SERVICE(TEST_INTF) = TEST_SERVICE;
+  AMBUS_OBJECT(TEST_INTF) = TEST_OBJECT;
+  AMBUS_INTERFACE(TEST_INTF) = TEST_INTERFACE;
+  AMBUS_CHECK(r, AMBUS_ADD_VTABLE(ambus, TEST_INTF, &obj), goto error);
+  AMBUS_CHECK(r, AMBUS_REQUEST_NAME(ambus, TEST_INTF), goto error);
+  test_call_async(ambus);
+  ambus_run(ambus);
+error:
+  ambus_free(ambus);
+  return 0;
+}