pax_global_header00006660000000000000000000000064152612761670014527gustar00rootroot0000000000000052 comment=770f4ce2a5aca0bcaf4df2ba58e21fa30e74b1bc libgbinder-1.1.54/000077500000000000000000000000001526127616700137205ustar00rootroot00000000000000libgbinder-1.1.54/.gitignore000066400000000000000000000003721526127616700157120ustar00rootroot00000000000000*~ debian/files debian/libgbinder debian/libgbinder-dev debian/libgbinder-tools debian/*.debhelper.log debian/*.debhelper debian/*.substvars debian/libgbinder.install debian/libgbinder-dev.install debian/tmp documentation.list installroot build RPMS libgbinder-1.1.54/AUTHORS000066400000000000000000000011461526127616700147720ustar00rootroot00000000000000Slava Monich Matti Lehtimäki Franz-Josef Haider Enni Hämäläinen Andrew Branson Rinigus George Hopkins Bart Ribbers Gary Wang Eugenio Paolantonio Alessandro Astone Martin Kampas Nikita Ukhrenkov Ratchanan Srirattanamet Takuya Wakazono libgbinder-1.1.54/LICENSE000066400000000000000000000030721526127616700147270ustar00rootroot00000000000000Copyright (C) 2025-2026 Jolla Mobile Ltd. Copyright (C) 2018-2026 Slava Monich Copyright (C) 2018-2024 Jolla Ltd. 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. libgbinder-1.1.54/Makefile000066400000000000000000000172161526127616700153670ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- # # LIBGLIBUTIL_PATH can be defined to point to libglibutil root directory # for side-by-side build. # .PHONY: clean all debug release test .PHONY: print_debug_so print_release_so .PHONY: print_debug_lib print_release_lib print_coverage_lib .PHONY: print_debug_link print_release_link .PHONY: print_debug_path print_release_path # # Library version # VERSION_MAJOR := 1 VERSION_MINOR := 1 VERSION_RELEASE := 54 # Version for pkg-config PCVERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE) # # Default target # all: debug release pkgconfig # # Required packages # PKGS = glib-2.0 gobject-2.0 ifeq ($(LIBGLIBUTIL_PATH),) # Assume that libglibutil devel package is installed PKGS += libglibutil else # Side-by-side build INCLUDES += -I$(LIBGLIBUTIL_PATH)/include DEBUG_LIBS = -L$(LIBGLIBUTIL_PATH)/build/debug -lglibutil RELEASE_LIBS = -L$(LIBGLIBUTIL_PATH)/build/release -lglibutil DEBUG_DEPS = libglibutil_debug RELEASE_DEPS = libglibutil_release .PHONY: libglibutil_debug libglibutil_release libglibutil_debug: $(MAKE) -C $(LIBGLIBUTIL_PATH) debug libglibutil_release: $(MAKE) -C $(LIBGLIBUTIL_PATH) release endif # # Library name # NAME = gbinder LIB_NAME = lib$(NAME) LIB_DEV_SYMLINK = $(LIB_NAME).so LIB_SYMLINK1 = $(LIB_DEV_SYMLINK).$(VERSION_MAJOR) LIB_SYMLINK2 = $(LIB_SYMLINK1).$(VERSION_MINOR) LIB_SONAME = $(LIB_SYMLINK1) LIB_SO = $(LIB_SONAME).$(VERSION_MINOR).$(VERSION_RELEASE) LIB = $(LIB_NAME).a # # Sources # SRC = \ gbinder_bridge.c \ gbinder_buffer.c \ gbinder_client.c \ gbinder_config.c \ gbinder_driver.c \ gbinder_eventloop.c \ gbinder_fmq.c \ gbinder_io_32.c \ gbinder_io_64.c \ gbinder_ipc.c \ gbinder_local_object.c \ gbinder_local_reply.c \ gbinder_local_request.c \ gbinder_log.c \ gbinder_proxy_object.c \ gbinder_reader.c \ gbinder_remote_object.c \ gbinder_remote_reply.c \ gbinder_remote_request.c \ gbinder_rpc_protocol.c \ gbinder_servicename.c \ gbinder_servicepoll.c \ gbinder_writer.c SRC += \ gbinder_servicemanager.c \ gbinder_servicemanager_aidl.c \ gbinder_servicemanager_aidl2.c \ gbinder_servicemanager_aidl3.c \ gbinder_servicemanager_aidl5.c \ gbinder_servicemanager_aidl6.c \ gbinder_servicemanager_hidl.c SRC += \ gbinder_system.c # # Directories # SRC_DIR = src INCLUDE_DIR = include BUILD_DIR = build DEBUG_BUILD_DIR = $(BUILD_DIR)/debug RELEASE_BUILD_DIR = $(BUILD_DIR)/release COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage # # Tools and flags # PKG_CONFIG ?= pkg-config CC ?= $(CROSS_COMPILE)gcc STRIP ?= strip LD = $(CC) WARNINGS = -Wall -Wstrict-aliasing -Wunused-result DEFINES += -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32 \ -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_MAX_ALLOWED INCLUDES += -I$(INCLUDE_DIR) BASE_FLAGS = -fPIC -g FULL_CFLAGS = $(BASE_FLAGS) $(CFLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) \ -MMD -MP $(shell $(PKG_CONFIG) --cflags $(PKGS)) FULL_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS) -shared -Wl,-soname,$(LIB_SONAME) \ $(shell $(PKG_CONFIG) --libs $(PKGS)) -lpthread KEEP_SYMBOLS ?= 0 DEBUG_LDFLAGS = $(FULL_LDFLAGS) $(DEBUG_LIBS) RELEASE_LDFLAGS = $(FULL_LDFLAGS) $(RELEASE_LIBS) DEBUG_CFLAGS = $(FULL_CFLAGS) -DDEBUG RELEASE_CFLAGS = $(FULL_CFLAGS) -O2 COVERAGE_CFLAGS = $(FULL_CFLAGS) -O0 --coverage -fprofile-update=atomic # # Files # PKGCONFIG = $(BUILD_DIR)/$(LIB_NAME).pc DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) COVERAGE_OBJS = $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o) DEBUG_SO = $(DEBUG_BUILD_DIR)/$(LIB_SO) RELEASE_SO = $(RELEASE_BUILD_DIR)/$(LIB_SO) DEBUG_LINK = $(DEBUG_BUILD_DIR)/$(LIB_SYMLINK1) RELEASE_LINK = $(RELEASE_BUILD_DIR)/$(LIB_SYMLINK1) DEBUG_DEV_LINK = $(DEBUG_BUILD_DIR)/$(LIB_DEV_SYMLINK) RELEASE_DEV_LINK = $(RELEASE_BUILD_DIR)/$(LIB_DEV_SYMLINK) DEBUG_LIB = $(DEBUG_BUILD_DIR)/$(LIB) RELEASE_LIB = $(RELEASE_BUILD_DIR)/$(LIB) COVERAGE_LIB = $(COVERAGE_BUILD_DIR)/$(LIB) # # Dependencies # DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) ifneq ($(MAKECMDGOALS),clean) ifneq ($(strip $(DEPS)),) -include $(DEPS) endif endif $(PKGCONFIG): | $(BUILD_DIR) $(DEBUG_OBJS) $(DEBUG_SO): | $(DEBUG_BUILD_DIR) $(DEBUG_DEPS) $(RELEASE_OBJS) $(RELEASE_SO): | $(RELEASE_BUILD_DIR) $(RELEASE_DEPS) $(COVERAGE_OBJS) $(COVERAGE_LIB): | $(COVERAGE_BUILD_DIR) $(DEBUG_LINK): | $(DEBUG_LIB) $(RELEASE_LINK): | $(RELEASE_LIB) $(DEBUG_DEV_LINK): | $(DEBUG_LINK) $(RELEASE_DEV_LINK): | $(RELEASE_LINK) # # Rules # debug: $(DEBUG_SO) $(DEBUG_LINK) $(DEBUG_DEV_LINK) release: $(RELEASE_SO) $(RELEASE_LINK) $(RELEASE_DEV_LINK) debug_lib: $(DEBUG_LIB) release_lib: $(RELEASE_LIB) coverage_lib: $(COVERAGE_LIB) pkgconfig: $(PKGCONFIG) print_debug_so: @echo $(DEBUG_SO) print_release_so: @echo $(RELEASE_SO) print_debug_lib: @echo $(DEBUG_LIB) print_release_lib: @echo $(RELEASE_LIB) print_coverage_lib: @echo $(COVERAGE_LIB) print_debug_link: @echo $(DEBUG_LINK) print_release_link: @echo $(RELEASE_LINK) print_debug_path: @echo $(DEBUG_BUILD_DIR) print_release_path: @echo $(RELEASE_BUILD_DIR) clean: $(MAKE) -C tools clean $(MAKE) -C test clean $(MAKE) -C unit clean rm -fr test/coverage/results test/coverage/*.gcov rm -f *~ $(SRC_DIR)/*~ $(INCLUDE_DIR)/*~ rm -fr $(BUILD_DIR) RPMS installroot rm -fr debian/tmp debian/libgbinder debian/libgbinder-dev rm -f documentation.list debian/files debian/*.substvars rm -f debian/*.debhelper.log debian/*.debhelper debian/*~ rm -f debian/libgbinder.install debian/libgbinder-dev.install test: $(MAKE) -C unit test $(BUILD_DIR): mkdir -p $@ $(DEBUG_BUILD_DIR): mkdir -p $@ $(RELEASE_BUILD_DIR): mkdir -p $@ $(COVERAGE_BUILD_DIR): mkdir -p $@ $(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 $@ $(COVERAGE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c $(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(DEBUG_SO): $(DEBUG_OBJS) $(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) -o $@ $(RELEASE_SO): $(RELEASE_OBJS) $(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) -o $@ ifeq ($(KEEP_SYMBOLS),0) $(STRIP) $@ endif $(DEBUG_LIB): $(DEBUG_OBJS) $(AR) rc $@ $? ranlib $@ $(RELEASE_LIB): $(RELEASE_OBJS) $(AR) rc $@ $? ranlib $@ $(DEBUG_LINK): ln -sf $(LIB_SO) $@ $(RELEASE_LINK): ln -sf $(LIB_SO) $@ $(DEBUG_DEV_LINK): ln -sf $(LIB_SYMLINK1) $@ $(RELEASE_DEV_LINK): ln -sf $(LIB_SYMLINK1) $@ $(COVERAGE_LIB): $(COVERAGE_OBJS) $(AR) rc $@ $? ranlib $@ # # LIBDIR usually gets substituted with arch specific dir. # It's relative in deb build and can be whatever in rpm build. # LIBDIR ?= usr/lib ABS_LIBDIR := $(shell echo /$(LIBDIR) | sed -r 's|/+|/|g') $(PKGCONFIG): $(LIB_NAME).pc.in Makefile sed -e 's|@version@|$(PCVERSION)|g' -e 's|@libdir@|$(ABS_LIBDIR)|g' $< > $@ debian/%.install: debian/%.install.in sed 's|@LIBDIR@|$(LIBDIR)|g' $< > $@ # # Install # INSTALL_PERM = 644 INSTALL = install INSTALL_DIRS = $(INSTALL) -d INSTALL_FILES = $(INSTALL) -m $(INSTALL_PERM) INSTALL_LIB_DIR = $(DESTDIR)$(ABS_LIBDIR) INSTALL_INCLUDE_DIR = $(DESTDIR)/usr/include/$(NAME) INSTALL_PKGCONFIG_DIR = $(DESTDIR)$(ABS_LIBDIR)/pkgconfig install: $(INSTALL_LIB_DIR) $(INSTALL) -m 755 $(RELEASE_SO) $(INSTALL_LIB_DIR) ln -sf $(LIB_SO) $(INSTALL_LIB_DIR)/$(LIB_SYMLINK2) ln -sf $(LIB_SYMLINK2) $(INSTALL_LIB_DIR)/$(LIB_SYMLINK1) install-dev: install $(INSTALL_INCLUDE_DIR) $(INSTALL_PKGCONFIG_DIR) $(INSTALL_FILES) $(INCLUDE_DIR)/*.h $(INSTALL_INCLUDE_DIR) $(INSTALL_FILES) $(PKGCONFIG) $(INSTALL_PKGCONFIG_DIR) ln -sf $(LIB_SYMLINK1) $(INSTALL_LIB_DIR)/$(LIB_DEV_SYMLINK) $(INSTALL_LIB_DIR): $(INSTALL_DIRS) $@ $(INSTALL_INCLUDE_DIR): $(INSTALL_DIRS) $@ $(INSTALL_PKGCONFIG_DIR): $(INSTALL_DIRS) $@ libgbinder-1.1.54/README000066400000000000000000000030201526127616700145730ustar00rootroot00000000000000GLib-style interface to binder (Android IPC mechanism) Key features: 1. Integration with GLib event loop 2. Detection of 32 vs 64 bit kernel at runtime 3. Asynchronous transactions that don't block the event thread 4. Stable service manager and low-level transaction APIs Android keeps changing both low-level RPC and service manager protocols from version to version. To counter that, libgbinder implements configirable backends for different variants of those, and yet keeping its own API unchanged. Configuration is loaded from [Protocol] and [ServiceManager] sections of /etc/gbinder.conf file. The keys are binder device names or the special Default value, the value is the identifier of the protocol or service manager variant, respectively. In addition to reading /etc/gbinder.conf if it exists, /etc/gbinder.d directory is scanned for .conf files, the file list is sorted, files are loaded one by one, overwriting the entries loaded from /etc/gbinder.conf or from the previously processed file. Known protocol and service manager variants are aidl, aidl2, aidl3 and hidl. This list is expected to expand further in the future. The default configuration is as follows: [Protocol] Default = aidl /dev/binder = aidl /dev/hwbinder = hidl [ServiceManager] Default = aidl /dev/binder = aidl /dev/hwbinder = hidl Alternatively and preferably, one can specify the desired Android API level: [General] ApiLevel = 29 and let libgbinder pick the appropriate preset. Full list of presets can be found in src/gbinder_config.c libgbinder-1.1.54/debian/000077500000000000000000000000001526127616700151425ustar00rootroot00000000000000libgbinder-1.1.54/debian/changelog000066400000000000000000000512031526127616700170150ustar00rootroot00000000000000libgbinder (1.1.54) unstable; urgency=low * Drop gbinder_cleanup in favor of gutil_cleanup -- Slava Monich Wed, 07 Oct 2026 01:54:25 +0300 libgbinder (1.1.53) unstable; urgency=low * Restore original handles for returning Binder proxies * Enforce test timeouts when the main loop is blocked -- Slava Monich Fri, 02 Oct 2026 05:38:57 +0300 libgbinder (1.1.52) unstable; urgency=low * Support both checkService variants for Android 15 * Prevent reuse of servicemanager instance table -- Slava Monich Thu, 23 Jul 2026 17:55:23 +0300 libgbinder (1.1.51) unstable; urgency=low * Added support for AIDL registration notifications * Droped (broken?) gbinder_servicemanager_aidl4 * Moved tools into a separate directory -- Slava Monich Wed, 22 Jul 2026 04:07:29 +0300 libgbinder (1.1.50) unstable; urgency=low * Fixed proxy issues -- Slava Monich Wed, 01 Jul 2026 17:51:34 +0300 libgbinder (1.1.49) unstable; urgency=low * Added gbinder_reader_skip_parcelable() API * Skip objects consumed by gbinder_reader_read_parcelable() * Prohibit further reads from finished parcelables -- Slava Monich Mon, 29 Jun 2026 16:25:44 +0300 libgbinder (1.1.48) unstable; urgency=low * Added gbinder_reader_start/finish_parcelable() API * Added gbinder_reader_read_parcelable2() API * Prevent creation of corrupted coverage profiles * Fixed race condition in /proxy_object/param test -- Slava Monich Mon, 22 Jun 2026 01:12:50 +0300 libgbinder (1.1.47) unstable; urgency=low * Added gbinder_writer_start/finish_parcelable() -- Slava Monich Thu, 28 May 2026 02:14:36 +0300 libgbinder (1.1.46) unstable; urgency=low * Fixed potential string length overflow * Use release build for unit testing -- Slava Monich Sun, 24 May 2026 19:13:49 +0300 libgbinder (1.1.45) unstable; urgency=low * Add support for Android 15 (API level 35) * Add support for Android 16 (API level 36) * Conform to openSUSE shared library packaging policy -- Slava Monich Mon, 23 Mar 2026 04:54:02 +0200 libgbinder (1.1.44) unstable; urgency=low * Added gbinder_bridge_new3 (direct-name bridging) -- Slava Monich Tue, 03 Mar 2026 03:49:47 +0200 libgbinder (1.1.43) unstable; urgency=low * Add support for writing AIDL FMQs * Add missing implementation for gbinder_writer_append_fds * Explicitly declare function parameters for C23 compatibility -- Slava Monich Wed, 17 Sep 2025 04:02:48 +0300 libgbinder (1.1.42) unstable; urgency=low * Delete name watches with no handlers * Register for sm notifications asynchronously * Correct stability field wire format on Android 12 -- Slava Monich Wed, 11 Dec 2024 06:40:00 +0200 libgbinder (1.1.41) unstable; urgency=low * Fix sending NULL binder object * Added gbinder_reader_read_nullable_string8 * Check if counted strings are NULL-terminated -- Slava Monich Sat, 02 Nov 2024 05:13:22 +0200 libgbinder (1.1.40) unstable; urgency=low * Make stability field of local object configurable * Make more unit tests independent on system config * Use UNDECLARED stability for NULL binders -- Slava Monich Thu, 18 Jul 2024 05:14:28 +0300 libgbinder (1.1.39) unstable; urgency=low * Eliminate defects found by Coverity -- Slava Monich Sun, 05 May 2024 20:03:44 +0300 libgbinder (1.1.38) unstable; urgency=low * Fixed byte array padding -- Slava Monich Sat, 02 Mar 2024 02:11:50 +0200 libgbinder (1.1.37) unstable; urgency=low * Fixed gbinder_driver_reply_data return value -- Slava Monich Mon, 26 Feb 2024 16:22:29 +0200 libgbinder (1.1.36) unstable; urgency=low * Support pkg-config cross-compilation * Fixed handling of UTF-16 surrogate pairs -- Slava Monich Sat, 10 Feb 2024 05:04:13 +0200 libgbinder (1.1.35) unstable; urgency=low * Make unit tests independent on system config * Use MAKE var instead of explicitly calling make -- Slava Monich Thu, 23 Nov 2023 01:42:56 +0200 libgbinder (1.1.34) unstable; urgency=low * Fixed binder-call help message * Require glib 2.32 -- Slava Monich Sun, 30 Apr 2023 06:04:38 +0300 libgbinder (1.1.33) unstable; urgency=low * Fixed GBinderWriterType for byte and int32 -- Slava Monich Sun, 26 Feb 2023 03:13:49 +0200 libgbinder (1.1.32) unstable; urgency=low * Improved reliability of unit tests -- Slava Monich Mon, 23 Jan 2023 11:48:00 +0200 libgbinder (1.1.31) unstable; urgency=low * Fixed serialization issues on big-endian * Refactored binder simulation for unit tests -- Slava Monich Wed, 04 Jan 2023 19:10:37 +0200 libgbinder (1.1.30) unstable; urgency=low * Fixed BC_ACQUIRE_DONE encoding -- Slava Monich Sun, 04 Dec 2022 19:50:10 +0200 libgbinder (1.1.29) unstable; urgency=low * Added gbinder_writer_append_struct_vec -- Slava Monich Fri, 25 Nov 2022 00:01:41 +0200 libgbinder (1.1.28) unstable; urgency=low * Allow to pass negative number as a parameter to binder-call * Compile tools as PIE -- Slava Monich Tue, 22 Nov 2022 18:21:29 +0200 libgbinder (1.1.27) unstable; urgency=low * Added gbinder_writer_append_struct() -- Slava Monich Thu, 17 Nov 2022 03:25:37 +0200 libgbinder (1.1.26) unstable; urgency=low * Use BINDER_TYPE_HANDLE for NULL binders * Fixed binder object encoding in aidl3 protocol -- Slava Monich Fri, 14 Oct 2022 16:48:29 +0300 libgbinder (1.1.25) unstable; urgency=low * Handle RPC protocol change at run time -- Slava Monich Tue, 19 Jul 2022 02:02:41 +0300 libgbinder (1.1.24) unstable; urgency=low * Bumped debhelper compat level to 7 -- Slava Monich Thu, 14 Jul 2022 01:32:11 +0300 libgbinder (1.1.23) unstable; urgency=low * Fixed Debian build dependencies -- Slava Monich Thu, 23 Jun 2022 20:09:35 +0300 libgbinder (1.1.22) unstable; urgency=low * Added support for Android 12 (API level 31) * Made binder device configurable for binder-dump -- Slava Monich Wed, 22 Jun 2022 17:02:20 +0300 libgbinder (1.1.21) unstable; urgency=low * Properly finalize GBinderIpc and GBinderServicePoll -- Slava Monich Mon, 20 Jun 2022 18:53:26 +0300 libgbinder (1.1.20) unstable; urgency=low * Made RPC protocol selectable at runtime -- Slava Monich Sat, 11 Jun 2022 02:49:49 +0300 libgbinder (1.1.19) unstable; urgency=low * Added reader and writer for aidl parcelables -- Slava Monich Sun, 20 Feb 2022 03:26:24 +0200 libgbinder (1.1.18) unstable; urgency=low * Disassociate auto-created proxies to stop them from piling up -- Slava Monich Sat, 08 Jan 2022 15:35:56 +0200 libgbinder (1.1.17) unstable; urgency=low * Don't release remote proxy handle too early (sometimes hever) -- Slava Monich Fri, 07 Jan 2022 14:43:51 +0200 libgbinder (1.1.16) unstable; urgency=low * Make sure stale object pointers don't hang around * Properly shut down remote object inside the proxy * Read ref_count from GObject atomically -- Slava Monich Mon, 03 Jan 2022 13:58:44 +0200 libgbinder (1.1.15) unstable; urgency=low * Added readers and writers for int8 and int16 -- Slava Monich Mon, 27 Dec 2021 15:13:23 +0200 libgbinder (1.1.14) unstable; urgency=low * Support for FMQ (Fast Message Queues) * Support for Android 11 (API level 30) * Made GBinderReader API slightly more NULL tolerant * Added gbinder_client_rpc_header() * Added gbinder_reader_get_data() * Added gbinder_writer_get_data() * Added gbinder_servicemanager_device() * Added gbinder_local_reply_append_fd() -- Slava Monich Wed, 24 Nov 2021 17:15:48 +0200 libgbinder (1.1.13) unstable; urgency=low * Added gbinder_writer_strdup() * Added gbinder_writer_append_hidl_string_copy() * Dropped pkgconfig requirement for devel package -- Slava Monich Thu, 28 Oct 2021 14:31:01 +0300 libgbinder (1.1.12) unstable; urgency=low * Added binder-call test tool -- Slava Monich Fri, 24 Sep 2021 16:46:05 +0300 libgbinder (1.1.11) unstable; urgency=low * Fix potential deadlock in gbinder_ipc_looper_free() * Fix occasional crashes in pthread_setname_np() * Fix unit tests on some musl-based systems * Make unit tests comptible with glib < 2.36 * Bump libglibutil requirement for debian build -- Slava Monich Thu, 02 Sep 2021 12:32:39 +0300 libgbinder (1.1.10) unstable; urgency=low * Release dead binder nodes * Use gutil_memdup() instead of g_memdup() -- Slava Monich Mon, 10 May 2021 02:36:43 +0300 libgbinder (1.1.9) unstable; urgency=low * Include definition of _IOC_SIZE -- Slava Monich Tue, 20 Apr 2021 12:52:41 +0300 libgbinder (1.1.8) unstable; urgency=low * Handle out-of-range transaction codes -- Slava Monich Fri, 16 Apr 2021 19:11:14 +0300 libgbinder (1.1.7) unstable; urgency=low * Dropped use of g_main_context_invoke_full() -- Slava Monich Wed, 31 Mar 2021 23:10:37 +0300 libgbinder (1.1.6) unstable; urgency=low * Implemented support for passing object over the bridge * Retry service name registration * Wait for completion of the reply * Fixed death handling by GBinderBridge * Added gbinder_bridge_new2() * Added -s option to binder-bridge * Fixed invalid slice deallocation * Made unit tests more reliable * Make sure that libgbinder doesn't block on exit -- Slava Monich Tue, 02 Mar 2021 18:18:03 +0200 libgbinder (1.1.5) unstable; urgency=low * Fixed gbinder_remote_reply_copy_to_local() for empty replies * Improved binder simulation * Added GBinderBridge object * Added proxy_object and bridge unit tests * Added binder-bridge to libgbinder-tools package -- Slava Monich Fri, 29 Jan 2021 04:00:09 +0200 libgbinder (1.1.4) unstable; urgency=low * Fixed a threading issue * Decode NULL object reference * Added new basic HIDL types * Set TF_ACCEPT_FDS transaction flag * Added servicemanager_hidl unit test -- Slava Monich Thu, 21 Jan 2021 03:34:45 +0200 libgbinder (1.1.3) unstable; urgency=low * Improved unit test coverage -- Slava Monich Wed, 23 Dec 2020 21:48:27 +0200 libgbinder (1.1.2) unstable; urgency=low * Fixed random unit text failures -- Slava Monich Wed, 23 Dec 2020 12:39:22 +0200 libgbinder (1.1.1) unstable; urgency=low * Handle corner cases for abandoned loopers * Pass 0x0f priority to aidl2 service list request. * Improved binder simulation for unit tests * Added servicemanager_aidl unit test -- Slava Monich Tue, 22 Dec 2020 15:15:10 +0200 libgbinder (1.1.0) unstable; urgency=low * Made RPC protocol configurable per binder device * Made service managers configurable per binder device * Added support for multiple config files * Added "aidl2" variant of service manager * Added "aidl2" variant of RPC protocol * Added support for API level presets -- Slava Monich Fri, 04 Dec 2020 13:47:26 +0200 libgbinder (1.0.47) unstable; urgency=low * Make library executable on RPM based systems -- Slava Monich Sat, 19 Sep 2020 20:14:20 +0300 libgbinder (1.0.46) unstable; urgency=low * Make sure we drop fds that are going to be closed * Better diagnostics for polling failures * Package binder-list and binder-ping utilities -- Slava Monich Fri, 31 Jul 2020 02:04:38 +0300 libgbinder (1.0.45) unstable; urgency=low * Always provide data buffer for hidl vector -- Slava Monich Mon, 27 Jul 2020 23:19:25 +0300 libgbinder (1.0.44) unstable; urgency=low * Never drop valid incoming transactions -- Slava Monich Mon, 29 Jun 2020 17:05:22 +0300 libgbinder (1.0.43) unstable; urgency=low * Hide internal symbols * Respect arch specific lib dir * Allow strip command to be replaced via environment variable -- Slava Monich Wed, 03 Jun 2020 15:15:40 +0300 libgbinder (1.0.42) unstable; urgency=low * Allow GBinderClient to use multiple interfaces -- Slava Monich Tue, 05 May 2020 19:56:39 +0300 libgbinder (1.0.41) unstable; urgency=low * Stop using GUtilIdlePool -- Slava Monich Tue, 14 Apr 2020 12:36:54 +0300 libgbinder (1.0.40) unstable; urgency=low * Support integration with non-glib event loops -- Slava Monich Thu, 09 Apr 2020 17:22:12 +0300 libgbinder (1.0.39) unstable; urgency=low * Adapted to side-by-side linking -- Slava Monich Thu, 02 Apr 2020 18:07:16 +0300 libgbinder (1.0.38) unstable; urgency=low * Allow to configure log level via environment -- Slava Monich Thu, 02 Apr 2020 00:12:01 +0300 libgbinder (1.0.37) unstable; urgency=low * Allow side-by-side linking with libglibutil * Fixed compilation warnings -- Slava Monich Tue, 17 Mar 2020 20:15:11 +0200 libgbinder (1.0.36) unstable; urgency=low * Allow overwriting CC -- Slava Monich Mon, 16 Mar 2020 16:15:24 +0200 libgbinder (1.0.35) unstable; urgency=low * Added binder-ping example -- Slava Monich Tue, 25 Feb 2020 13:58:19 +0200 libgbinder (1.0.34) unstable; urgency=low * Better cleanup on unload to prevent crashes on exit * Fixed rare memory leak in GBinderServiceManager -- Slava Monich Mon, 16 Dec 2019 12:25:56 +0200 libgbinder (1.0.33) unstable; urgency=low * Reuse loopers -- Slava Monich Fri, 13 Sep 2019 15:57:47 +0300 libgbinder (1.0.32) unstable; urgency=low * Refuse to perform transactions with dead objects -- Slava Monich Fri, 17 May 2019 15:57:30 +0300 libgbinder (1.0.31) unstable; urgency=low * Invalidate handle when remote object dies -- Slava Monich Mon, 13 May 2019 18:05:35 +0300 libgbinder (1.0.30) unstable; urgency=low * Added gbinder_local_object_new() * Added gbinder_remote_object_ipc() -- Slava Monich Wed, 20 Feb 2019 11:59:08 +0200 libgbinder (1.0.29) unstable; urgency=low * Added gbinder_servicemanager_new_local_object2() -- Slava Monich Thu, 14 Feb 2019 18:17:53 +0300 libgbinder (1.0.28) unstable; urgency=low * Set type for local nulls to BINDER_TYPE_WEAK_BINDER -- Slava Monich Tue, 29 Jan 2019 02:49:10 +0200 libgbinder (1.0.27) unstable; urgency=low * Fixed outgoing oneway transactions -- Slava Monich Thu, 24 Jan 2019 18:55:16 +0200 libgbinder (1.0.26) unstable; urgency=low * Implement PING and INTERFACE transactions * Add GBinderServiceName API * Added gbinder_reader_read_string16_utf16() -- Slava Monich Wed, 23 Jan 2019 17:43:41 +0200 libgbinder (1.0.25) unstable; urgency=low * Added ServiceManager presence API gbinder_servicemanager_wait() gbinder_servicemanager_is_present() gbinder_servicemanager_add_presence_handler() -- Slava Monich Tue, 22 Jan 2019 16:03:57 +0200 libgbinder (1.0.24) unstable; urgency=low * Revert "Make sure NULL objects are passed as BINDER_TYPE_WEAK_HANDLE" -- Slava Monich Fri, 18 Jan 2019 21:36:32 +0200 libgbinder (1.0.23) unstable; urgency=low * Added gbinder_reader_read_hidl_string_c() -- Slava Monich Tue, 15 Jan 2019 15:16:41 +0200 libgbinder (1.0.22) unstable; urgency=low * Added gbinder_client_interface() -- Slava Monich Thu, 10 Jan 2019 14:09:44 +0300 libgbinder (1.0.21) unstable; urgency=low * Added API to overwrite prefix length -- Slava Monich Tue, 18 Dec 2018 14:05:14 +0200 libgbinder (1.0.20) unstable; urgency=low * Added API to block incoming requests -- Slava Monich Mon, 17 Dec 2018 16:06:43 +0200 libgbinder (1.0.19) unstable; urgency=low * Added GBinderWriter memory allocation and cleanup API -- Slava Monich Fri, 14 Dec 2018 16:27:51 +0200 libgbinder (1.0.18) unstable; urgency=low * Implemented support for file descritors * Allow GBinderClient without RPC header * Added binder-dump test -- Slava Monich Mon, 10 Dec 2018 13:17:22 +0200 libgbinder (1.0.17) unstable; urgency=low * Added gbinder_writer_append_string16_utf16() * Added gbinder_reader_read_nullable_string16_utf16() -- Slava Monich Fri, 07 Dec 2018 02:54:07 +0200 libgbinder (1.0.16) unstable; urgency=low * Added GBinderHidlVec and GBinderHidlString types * Added gbinder_reader_copy() -- Slava Monich Thu, 06 Dec 2018 19:03:32 +0200 libgbinder (1.0.15) unstable; urgency=low * Implemented service polling for old servicemanager * Added new tests and improved coverage for existing ones -- Slava Monich Wed, 05 Dec 2018 12:11:34 +0200 libgbinder (1.0.14) unstable; urgency=low * Changed bool padding from 0xff to 0x00 -- Slava Monich Tue, 27 Nov 2018 17:20:18 +0200 libgbinder (1.0.13) unstable; urgency=low * Support for service registration notifications * Make sure looper is started before gbinder_ipc_looper_new() returns -- Slava Monich Sat, 17 Nov 2018 01:52:28 +0200 libgbinder (1.0.12) unstable; urgency=low * Add byte array reader and writer -- Slava Monich Wed, 31 Oct 2018 17:04:38 +0300 libgbinder (1.0.11) unstable; urgency=low * Use BINDER_TYPE_WEAK_HANDLE for NULL objects -- Slava Monich Wed, 24 Oct 2018 18:57:28 +0300 libgbinder (1.0.10) unstable; urgency=low * Fixed dependencies for unit tests * Plugged memory leak in unit_reader -- Slava Monich Wed, 10 Oct 2018 14:44:44 +0300 libgbinder (1.0.9) unstable; urgency=low * Respect strong refs to GBinderLocalObject * Added gbinder_reader_read_hidl_struct macro * Added gbinder_reader_read_hidl_type_vec macro * Added gbinder_reader_read_hidl_byte_vec macro -- Slava Monich Mon, 08 Oct 2018 11:41:33 +0300 libgbinder (1.0.8) unstable; urgency=low * Added gbinder_writer_append_hidl_vec() * Added Added gbinder_reader_read_hidl_vec() -- Slava Monich Tue, 25 Sep 2018 01:08:54 +0300 libgbinder (1.0.7) unstable; urgency=low * Added gbinder_remote_request_copy_to_local() * Added gbinder_remote_reply_copy_to_local() * Make sure RPC protocol matches servicemanager type -- Slava Monich Sun, 23 Sep 2018 22:47:16 +0300 libgbinder (1.0.6) unstable; urgency=low * Fixed GBinderServiceManager lifecycle management -- Slava Monich Fri, 21 Sep 2018 21:15:28 +0300 libgbinder (1.0.5) unstable; urgency=low * Added double and float support -- Slava Monich Fri, 17 Aug 2018 00:01:27 +0300 libgbinder (1.0.4) unstable; urgency=low * Handle transaction result on main thread -- Slava Monich Tue, 07 Aug 2018 11:19:29 +0300 libgbinder (1.0.3) unstable; urgency=low * Added gbinder_local_request_append_bool() * Added gbinder_local_reply_append_bool() -- Slava Monich Mon, 06 Aug 2018 19:15:29 +0300 libgbinder (1.0.2) unstable; urgency=low * Added gbinder_remote_request_sender_pid() * Added gbinder_remote_request_sender_euid() -- Slava Monich Wed, 25 Jul 2018 00:51:39 +0300 libgbinder (1.0.1) unstable; urgency=low * Initialize the buffer passed to the driver -- Slava Monich Thu, 19 Jul 2018 01:05:36 +0300 libgbinder (1.0.0) unstable; urgency=low * Initial release -- Slava Monich Thu, 12 Jul 2018 00:37:21 +0300 libgbinder-1.1.54/debian/compat000066400000000000000000000000021526127616700163400ustar00rootroot000000000000007 libgbinder-1.1.54/debian/control000066400000000000000000000013011526127616700165400ustar00rootroot00000000000000Source: libgbinder Section: libs Priority: optional Maintainer: Slava Monich Build-Depends: debhelper (>= 8.1.3), libglib2.0-dev (>= 2.0), libglibutil-dev (>= 1.0.83), flex, bison Standards-Version: 3.8.4 Package: libgbinder Section: libs Architecture: any Depends: libglibutil (>= 1.0.83), ${shlibs:Depends}, ${misc:Depends} Description: Binder client library Package: libgbinder-dev Section: libdevel Architecture: any Depends: libgbinder (= ${binary:Version}), libglibutil-dev (>= 1.0.83) Description: Development files for libgbinder Package: libgbinder-tools Section: utils Architecture: any Depends: libgbinder, ${misc:Depends} Description: Binder command line utilities libgbinder-1.1.54/debian/copyright000066400000000000000000000030721526127616700170770ustar00rootroot00000000000000Copyright (C) 2025-2026 Jolla Mobile Ltd. Copyright (C) 2018-2026 Slava Monich Copyright (C) 2018-2024 Jolla Ltd. 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. libgbinder-1.1.54/debian/libgbinder-dev.install.in000066400000000000000000000002171526127616700220140ustar00rootroot00000000000000debian/tmp/@LIBDIR@/libgbinder.so @LIBDIR@ debian/tmp/@LIBDIR@/pkgconfig/libgbinder.pc @LIBDIR@/pkgconfig debian/tmp/usr/include/* usr/include libgbinder-1.1.54/debian/libgbinder-tools.install000066400000000000000000000000351526127616700217670ustar00rootroot00000000000000debian/tmp/usr/bin/* usr/bin libgbinder-1.1.54/debian/libgbinder.install.in000066400000000000000000000000551526127616700212400ustar00rootroot00000000000000debian/tmp/@LIBDIR@/libgbinder.so.* @LIBDIR@ libgbinder-1.1.54/debian/rules000077500000000000000000000007031526127616700162220ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 LIBDIR=usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH) override_dh_auto_build: dh_auto_build -- LIBDIR=$(LIBDIR) release pkgconfig debian/libgbinder.install debian/libgbinder-dev.install dh_auto_build -- -C tools release override_dh_auto_install: dh_auto_install -- LIBDIR=$(LIBDIR) install-dev dh_auto_install -- -C tools %: dh $@ libgbinder-1.1.54/debian/source/000077500000000000000000000000001526127616700164425ustar00rootroot00000000000000libgbinder-1.1.54/debian/source/format000066400000000000000000000000151526127616700176510ustar00rootroot000000000000003.0 (native) libgbinder-1.1.54/include/000077500000000000000000000000001526127616700153435ustar00rootroot00000000000000libgbinder-1.1.54/include/gbinder.h000066400000000000000000000044761526127616700171410ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_H #define GBINDER_H /* Convenience header to pull in everything at once */ #include "gbinder_bridge.h" #include "gbinder_buffer.h" #include "gbinder_client.h" #include "gbinder_fmq.h" #include "gbinder_local_object.h" #include "gbinder_local_reply.h" #include "gbinder_local_request.h" #include "gbinder_reader.h" #include "gbinder_remote_object.h" #include "gbinder_remote_reply.h" #include "gbinder_remote_request.h" #include "gbinder_servicename.h" #include "gbinder_servicemanager.h" #include "gbinder_writer.h" #endif /* GBINDER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_bridge.h000066400000000000000000000063121526127616700204440ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * Copyright (C) 2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_BRIDGE_H #define GBINDER_BRIDGE_H #include "gbinder_types.h" /* Since 1.1.5 */ G_BEGIN_DECLS /* * A binder bridge object. * * For example, bridging "foobar" with interfaces ["example@1.0::IFoo", * "example@1.0::IBar"] would: * * 1. Watch "example@1.0::IFoo/foobar" and "example@1.0::IBar/foobar" on dest * 2. When those names appears, register objects on src under matching source * service names * 3. Pass calls coming from src to the dest objects and replies in the * opposite direction * 4. When dest objects disappear, remove the corresponding bridging objects * from src * * and so on. */ GBinderBridge* gbinder_bridge_new( const char* name, const char* const* ifaces, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.5 */ G_GNUC_WARN_UNUSED_RESULT; GBinderBridge* gbinder_bridge_new2( const char* src_name, const char* dest_name, const char* const* ifaces, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.6 */ G_GNUC_WARN_UNUSED_RESULT; /* Direct-name bridge (e.g. AIDL fq service names such as iface/instance). */ GBinderBridge* gbinder_bridge_new3( const char* src_name, const char* dest_name, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.44 */ G_GNUC_WARN_UNUSED_RESULT; void gbinder_bridge_free( GBinderBridge* bridge); /* Since 1.1.5 */ G_END_DECLS #endif /* GBINDER_BRIDGE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_buffer.h000066400000000000000000000037621526127616700204670ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 name of Jolla Ltd nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_BUFFER_H #define GBINDER_BUFFER_H #include G_BEGIN_DECLS struct gbinder_buffer { void* data; gsize size; }; void gbinder_buffer_free( GBinderBuffer* buf); G_END_DECLS #endif /* GBINDER_BUFFER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_client.h000066400000000000000000000070151526127616700204670ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_CLIENT_H #define GBINDER_CLIENT_H #include G_BEGIN_DECLS typedef struct gbinder_client_iface_info { const char* iface; guint32 last_code; } GBinderClientIfaceInfo; typedef void (*GBinderClientReplyFunc)( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data); GBinderClient* gbinder_client_new( GBinderRemoteObject* object, const char* iface); GBinderClient* gbinder_client_new2( GBinderRemoteObject* object, const GBinderClientIfaceInfo* ifaces, gsize count); /* since 1.0.42 */ GBinderClient* gbinder_client_ref( GBinderClient* client); void gbinder_client_unref( GBinderClient* client); const char* gbinder_client_interface( GBinderClient* client); /* since 1.0.22 */ const char* gbinder_client_interface2( GBinderClient* client, guint32 code); /* since 1.0.42 */ GBytes* gbinder_client_rpc_header( GBinderClient* client, guint32 code); /* since 1.1.14 */ GBinderLocalRequest* gbinder_client_new_request( GBinderClient* client); GBinderLocalRequest* gbinder_client_new_request2( GBinderClient* client, guint32 code); /* since 1.0.42 */ GBinderRemoteReply* gbinder_client_transact_sync_reply( GBinderClient* client, guint32 code, GBinderLocalRequest* req, int* status); int gbinder_client_transact_sync_oneway( GBinderClient* client, guint32 code, GBinderLocalRequest* req); gulong gbinder_client_transact( GBinderClient* client, guint32 code, guint32 flags, GBinderLocalRequest* req, GBinderClientReplyFunc reply, GDestroyNotify destroy, void* user_data); void gbinder_client_cancel( GBinderClient* client, gulong id); G_END_DECLS #endif /* GBINDER_CLIENT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_eventloop.h000066400000000000000000000152631526127616700212300ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_EVENTLOOP_H #define GBINDER_EVENTLOOP_H #include "gbinder_types.h" G_BEGIN_DECLS /* Since 1.0.40 */ typedef struct gbinder_eventloop_integration GBinderEventLoopIntegration; typedef void (*GBinderEventLoopCallbackFunc)(gpointer data); typedef struct gbinder_eventloop_timeout { const GBinderEventLoopIntegration* eventloop; } GBinderEventLoopTimeout; typedef struct gbinder_eventloop_callback { const GBinderEventLoopIntegration* eventloop; } GBinderEventLoopCallback; /** * Main event loop integration. There is only one main event loop in the * process (by definition). * * By default, GLib event loop is being used for callbacks and timeouts. * * It may be necessary to replace it with e.g. Qt event loop. Quite often * Qt event loop is implemented by QEventDispatcherGlib which is sitting * on top of GLib event and therefore works with the default implementation. * But it won't work with e.g. QEventDispatcherUNIX. * * For Qt programs that use QEventDispatcherUNIX, it needs to be replaced * with the one provided by libqbinder. */ typedef struct gbinder_eventloop_integration { /** * timeout_add * * Sets a function to be called at regular intervals (in milliseconds). * If the function returns G_SOURCE_REMOVE, timeout is automatically * destroyed (you must not call timeout_remove in this case). If the * function returns G_SOURCE_CONTINUE, it will be called again after * the same interval. */ GBinderEventLoopTimeout* (*timeout_add)(guint millis, GSourceFunc func, gpointer data); /** * timeout_remove * * Removes a pending timeout and destroys it. The caller makes sure that * argument is not NULL. Note that timeout is automatically destroyed if * the callback function returns G_SOURCE_REMOVE. */ void (*timeout_remove)(GBinderEventLoopTimeout* timeout); /** * callback_new * * Creates a callback object. It returns you a reference, you must * eventually pass the returned object to callback_unref to drop * this reference. * * Note that it doesn't automatically schedule the callback. You * must explicitly call callback_schedule to actually schedule it. * The finalize function is invoked regardless of whether callback * was cancelled or not. */ GBinderEventLoopCallback* (*callback_new)(GBinderEventLoopCallbackFunc fun, gpointer data, GDestroyNotify finalize); /** * callback_ref * * Increments the reference count. That prevents the object from being * deleted before you drop this reference. The caller makes sure that * argument is not NULL. */ void (*callback_ref)(GBinderEventLoopCallback* cb); /** * callback_unref * * Decrements the reference count (drops the reference). When reference * count reaches zero, the object gets deleted. The caller makes sure * that argument is not NULL. * * Note that calling callback_schedule temporarily adds an internal * reference until the callback is invoked or callback_cancel is called, * whichever happens first. */ void (*callback_unref)(GBinderEventLoopCallback* cb); /** * callback_schedule * * Schedules the callback to be invoked in the main loop at some point * in the future (but as soon as possible). The caller makes sure that * argument is not NULL. * * This adds an internal reference to the GBinderEventLoopCallback object * until the callback is invoked or callback_cancel is called, whichever * happens first. */ void (*callback_schedule)(GBinderEventLoopCallback* cb); /** * callback_cancel * * Makes sure that callback won't be invoked (if it hasn't been * invoked yet) and drops the internal reference. Does nothing * if the callback has already been invoked. The caller makes sure that * argument is not NULL. */ void (*callback_cancel)(GBinderEventLoopCallback* cb); /** * cleanup * * This function is called when event loop integration is being replaced * with a different one, or libgbinder is being unloaded. */ void (*cleanup)(void); /* Padding for future expansion */ void (*_reserved1)(void); void (*_reserved2)(void); void (*_reserved3)(void); void (*_reserved4)(void); void (*_reserved5)(void); void (*_reserved6)(void); void (*_reserved7)(void); void (*_reserved8)(void); void (*_reserved9)(void); /* * api_level will remain zero (and ignored) until we run out of * the above placeholders. Hopefully, forever. */ int api_level; } GBinderEventLoopIntegration; /** * gbinder_eventloop_set should be called before libgbinder creates any of * its internal threads. And it must be done from the main thread. */ void gbinder_eventloop_set( const GBinderEventLoopIntegration* loop); G_END_DECLS #endif /* GBINDER_EVENTLOOP_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_fmq.h000066400000000000000000000076221526127616700200000ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_FMQ_H #define GBINDER_FMQ_H #include G_BEGIN_DECLS /* Since 1.1.14 */ typedef enum gbinder_fmq_type { GBINDER_FMQ_TYPE_SYNC_READ_WRITE = 1, GBINDER_FMQ_TYPE_UNSYNC_WRITE } GBINDER_FMQ_TYPE; typedef enum gbinder_fmq_flags { GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG = 0x1, GBINDER_FMQ_FLAG_NO_RESET_POINTERS = 0x2 } GBINDER_FMQ_FLAGS; GBinderFmq* gbinder_fmq_new( gsize item_size, gsize max_num_items, GBINDER_FMQ_TYPE type, GBINDER_FMQ_FLAGS flags, gint fd, gsize buffer_size); GBinderFmq* gbinder_fmq_ref( GBinderFmq* fmq); void gbinder_fmq_unref( GBinderFmq* fmq); /* Functions for checking how many items are available in queue */ gsize gbinder_fmq_available_to_read( GBinderFmq* fmq); gsize gbinder_fmq_available_to_write( GBinderFmq* fmq); gsize gbinder_fmq_available_to_read_contiguous( GBinderFmq* fmq); gsize gbinder_fmq_available_to_write_contiguous( GBinderFmq* fmq); /* Functions for obtaining data pointer for zero copy read/write */ const void* gbinder_fmq_begin_read( GBinderFmq* fmq, gsize items); void* gbinder_fmq_begin_write( GBinderFmq* fmq, gsize items); /* Functions for ending zero copy read/write * The number of items must match the value provided to gbinder_fmq_begin_read * or gbinder_fmq_begin_write */ void gbinder_fmq_end_read( GBinderFmq* fmq, gsize items); void gbinder_fmq_end_write( GBinderFmq* fmq, gsize items); /* Regular read/write functions (non-zero-copy) */ gboolean gbinder_fmq_read( GBinderFmq* fmq, void* data, gsize items); gboolean gbinder_fmq_write( GBinderFmq* fmq, const void* data, gsize items); /* * Functions for waiting and waking message queue. * Requires configured event flag in message queue. */ int gbinder_fmq_wait_timeout( GBinderFmq* fmq, guint32 bit_mask, guint32* state, int timeout_ms); #define gbinder_fmq_try_wait(fmq, mask, state) \ gbinder_fmq_wait_timeout(fmq, mask, state, 0) #define gbinder_fmq_wait(fmq, mask, state) \ gbinder_fmq_wait_timeout(fmq, mask, state, -1) int gbinder_fmq_wake( GBinderFmq* fmq, guint32 bit_mask); G_END_DECLS #endif /* GBINDER_FMQ_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_local_object.h000066400000000000000000000050261526127616700216310ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_OBJECT_H #define GBINDER_LOCAL_OBJECT_H #include "gbinder_types.h" G_BEGIN_DECLS GBinderLocalObject* gbinder_local_object_new( GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc handler, void* user_data) /* Since 1.0.30 */ G_GNUC_WARN_UNUSED_RESULT; GBinderLocalObject* gbinder_local_object_ref( GBinderLocalObject* obj); void gbinder_local_object_unref( GBinderLocalObject* obj); void gbinder_local_object_drop( GBinderLocalObject* obj); GBinderLocalReply* gbinder_local_object_new_reply( GBinderLocalObject* obj); void gbinder_local_object_set_stability( GBinderLocalObject* self, GBINDER_STABILITY_LEVEL stability); /* Since 1.1.40 */ G_END_DECLS #endif /* GBINDER_LOCAL_OBJECT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_local_reply.h000066400000000000000000000071341526127616700215200ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_REPLY_H #define GBINDER_LOCAL_REPLY_H #include "gbinder_types.h" G_BEGIN_DECLS GBinderLocalReply* gbinder_local_reply_ref( GBinderLocalReply* reply); void gbinder_local_reply_unref( GBinderLocalReply* reply); void gbinder_local_reply_init_writer( GBinderLocalReply* reply, GBinderWriter* writer); void gbinder_local_reply_cleanup( GBinderLocalReply* reply, GDestroyNotify destroy, gpointer pointer); GBinderLocalReply* gbinder_local_reply_append_bool( GBinderLocalReply* reply, gboolean value); /* since 1.0.3 */ GBinderLocalReply* gbinder_local_reply_append_int32( GBinderLocalReply* reply, guint32 value); GBinderLocalReply* gbinder_local_reply_append_int64( GBinderLocalReply* reply, guint64 value); GBinderLocalReply* gbinder_local_reply_append_float( GBinderLocalReply* reply, gfloat value); GBinderLocalReply* gbinder_local_reply_append_double( GBinderLocalReply* reply, gdouble value); GBinderLocalReply* gbinder_local_reply_append_string8( GBinderLocalReply* reply, const char* str); GBinderLocalReply* gbinder_local_reply_append_string16( GBinderLocalReply* reply, const char* utf8); GBinderLocalReply* gbinder_local_reply_append_hidl_string( GBinderLocalReply* reply, const char* str); GBinderLocalReply* gbinder_local_reply_append_hidl_string_vec( GBinderLocalReply* reply, const char* strv[], gssize count); GBinderLocalReply* gbinder_local_reply_append_local_object( GBinderLocalReply* reply, GBinderLocalObject* obj); GBinderLocalReply* gbinder_local_reply_append_remote_object( GBinderLocalReply* reply, GBinderRemoteObject* obj); GBinderLocalReply* gbinder_local_reply_append_fd( GBinderLocalReply* reply, int fd); /* Since 1.1.14 */ G_END_DECLS #endif /* GBINDER_LOCAL_OBJECT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_local_request.h000066400000000000000000000071121526127616700220510ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 name of Jolla Ltd nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_REQUEST_H #define GBINDER_LOCAL_REQUEST_H #include "gbinder_types.h" G_BEGIN_DECLS GBinderLocalRequest* gbinder_local_request_ref( GBinderLocalRequest* request); void gbinder_local_request_unref( GBinderLocalRequest* request); void gbinder_local_request_init_writer( GBinderLocalRequest* request, GBinderWriter* writer); void gbinder_local_request_cleanup( GBinderLocalRequest* request, GDestroyNotify destroy, gpointer pointer); GBinderLocalRequest* gbinder_local_request_append_bool( GBinderLocalRequest* request, gboolean value); /* since 1.0.3 */ GBinderLocalRequest* gbinder_local_request_append_int32( GBinderLocalRequest* request, guint32 value); GBinderLocalRequest* gbinder_local_request_append_int64( GBinderLocalRequest* request, guint64 value); GBinderLocalRequest* gbinder_local_request_append_float( GBinderLocalRequest* request, gfloat value); GBinderLocalRequest* gbinder_local_request_append_double( GBinderLocalRequest* request, gdouble value); GBinderLocalRequest* gbinder_local_request_append_string8( GBinderLocalRequest* request, const char* str); GBinderLocalRequest* gbinder_local_request_append_string16( GBinderLocalRequest* request, const char* utf8); GBinderLocalRequest* gbinder_local_request_append_hidl_string( GBinderLocalRequest* request, const char* str); GBinderLocalRequest* gbinder_local_request_append_hidl_string_vec( GBinderLocalRequest* request, const char* strv[], gssize count); GBinderLocalRequest* gbinder_local_request_append_local_object( GBinderLocalRequest* request, GBinderLocalObject* obj); GBinderLocalRequest* gbinder_local_request_append_remote_object( GBinderLocalRequest* request, GBinderRemoteObject* obj); G_END_DECLS #endif /* GBINDER_LOCAL_OBJECT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_reader.h000066400000000000000000000160131526127616700204510ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_READER_H #define GBINDER_READER_H #include "gbinder_types.h" G_BEGIN_DECLS /* * Normally, the reader is initialized by gbinder_remote_reply_init_reader or * gbinder_remote_request_init_reader function. Note that the reader doesn't * copy the data nor does it reference the object which initialized it. The * caller must make sure that the data outlive the reader. * * Also, these functions are not NULL tolerant. The reader is normally * allocated on stack. */ struct gbinder_reader { gconstpointer d[6]; }; gboolean gbinder_reader_at_end( const GBinderReader* reader); gboolean gbinder_reader_read_byte( GBinderReader* reader, guchar* value); gboolean gbinder_reader_read_bool( GBinderReader* reader, gboolean* value); gboolean gbinder_reader_read_int8( GBinderReader* reader, gint8* value); /* Since 1.1.15 */ gboolean gbinder_reader_read_uint8( GBinderReader* reader, guint8* value); /* Since 1.1.15 */ gboolean gbinder_reader_read_int16( GBinderReader* reader, gint16* value); /* Since 1.1.15 */ gboolean gbinder_reader_read_uint16( GBinderReader* reader, guint16* value); /* Since 1.1.15 */ gboolean gbinder_reader_read_int32( GBinderReader* reader, gint32* value); gboolean gbinder_reader_read_uint32( GBinderReader* reader, guint32* value); gboolean gbinder_reader_read_int64( GBinderReader* reader, gint64* value); gboolean gbinder_reader_read_uint64( GBinderReader* reader, guint64* value); gboolean gbinder_reader_read_float( GBinderReader* reader, gfloat* value); gboolean gbinder_reader_read_double( GBinderReader* reader, gdouble* value); int gbinder_reader_read_fd( GBinderReader* reader); /* Since 1.0.18 */ int gbinder_reader_read_dup_fd( GBinderReader* reader) /* Since 1.0.18 */ G_GNUC_WARN_UNUSED_RESULT; gboolean gbinder_reader_read_nullable_object( GBinderReader* reader, GBinderRemoteObject** obj); GBinderRemoteObject* gbinder_reader_read_object( GBinderReader* reader) G_GNUC_WARN_UNUSED_RESULT; #define gbinder_reader_skip_object(reader) \ gbinder_reader_read_nullable_object(reader, NULL) GBinderBuffer* gbinder_reader_read_buffer( GBinderReader* reader) G_GNUC_WARN_UNUSED_RESULT; const void* gbinder_reader_read_parcelable( GBinderReader* reader, gsize* size); /* Since 1.1.19 */ const void* gbinder_reader_read_parcelable2( GBinderReader* reader, gsize* size, gboolean* ok); /* Since 1.1.48 */ gboolean gbinder_reader_start_parcelable( GBinderReader* reader, GBinderReader* parcelable, gboolean* non_null); /* Since 1.1.48 */ void gbinder_reader_finish_parcelable( GBinderReader* parcelable); /* Since 1.1.48 */ gboolean gbinder_reader_skip_parcelable( GBinderReader* reader); /* Since 1.1.49 */ const void* gbinder_reader_read_hidl_struct1( GBinderReader* reader, gsize size); /* Since 1.0.9 */ #define gbinder_reader_read_hidl_struct(reader,type) \ ((const type*)gbinder_reader_read_hidl_struct1(reader, sizeof(type))) const void* gbinder_reader_read_hidl_vec( GBinderReader* reader, gsize* count, gsize* elemsize); const void* gbinder_reader_read_hidl_vec1( GBinderReader* reader, gsize* count, guint expected_elemsize); /* Since 1.0.9 */ #define gbinder_reader_read_hidl_type_vec(reader,type,count) \ ((const type*)gbinder_reader_read_hidl_vec1(reader, count, sizeof(type))) #define gbinder_reader_read_hidl_byte_vec(reader,count) /* vec */ \ gbinder_reader_read_hidl_type_vec(reader,guint8,count) char* gbinder_reader_read_hidl_string( GBinderReader* reader) G_GNUC_WARN_UNUSED_RESULT; const char* gbinder_reader_read_hidl_string_c( GBinderReader* reader); /* Since 1.0.23 */ #define gbinder_reader_skip_hidl_string(reader) \ (gbinder_reader_read_hidl_string_c(reader) != NULL) char** gbinder_reader_read_hidl_string_vec( GBinderReader* reader); gboolean gbinder_reader_skip_buffer( GBinderReader* reader); const char* gbinder_reader_read_string8( GBinderReader* reader); gboolean gbinder_reader_read_nullable_string8( GBinderReader* reader, const char** out, gsize* out_len); /* Since 1.1.41 */ #define gbinder_reader_skip_nullable_string8(reader) \ gbinder_reader_read_nullable_string8(reader, NULL, NULL) char* gbinder_reader_read_string16( GBinderReader* reader) G_GNUC_WARN_UNUSED_RESULT; gboolean gbinder_reader_read_nullable_string16( GBinderReader* reader, char** out); gboolean gbinder_reader_read_nullable_string16_utf16( GBinderReader* reader, const gunichar2** out, gsize* len); /* Since 1.0.17 */ const gunichar2* gbinder_reader_read_string16_utf16( GBinderReader* reader, gsize* len); /* Since 1.0.26 */ gboolean gbinder_reader_skip_string16( GBinderReader* reader); const void* gbinder_reader_read_byte_array( GBinderReader* reader, gsize* len); /* Since 1.0.12 */ const void* gbinder_reader_get_data( const GBinderReader* reader, gsize* size); /* Since 1.1.14 */ gsize gbinder_reader_bytes_read( const GBinderReader* reader); gsize gbinder_reader_bytes_remaining( const GBinderReader* reader); void gbinder_reader_copy( GBinderReader* dest, const GBinderReader* src); /* Since 1.0.16 */ G_END_DECLS #endif /* GBINDER_READER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_remote_object.h000066400000000000000000000050551526127616700220340ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Jolla Ltd. * Copyright (C) 2018-2019 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_OBJECT_H #define GBINDER_REMOTE_OBJECT_H #include "gbinder_types.h" G_BEGIN_DECLS typedef void (*GBinderRemoteObjectNotifyFunc)( GBinderRemoteObject* obj, void* user_data); GBinderRemoteObject* gbinder_remote_object_ref( GBinderRemoteObject* obj); void gbinder_remote_object_unref( GBinderRemoteObject* obj); GBinderIpc* gbinder_remote_object_ipc( GBinderRemoteObject* obj); /* Since 1.0.30 */ gboolean gbinder_remote_object_is_dead( GBinderRemoteObject* obj); gulong gbinder_remote_object_add_death_handler( GBinderRemoteObject* obj, GBinderRemoteObjectNotifyFunc func, void* user_data); void gbinder_remote_object_remove_handler( GBinderRemoteObject* obj, gulong id); G_END_DECLS #endif /* GBINDER_REMOTE_OBJECT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_remote_reply.h000066400000000000000000000060171526127616700217200ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_REPLY_H #define GBINDER_REMOTE_REPLY_H #include G_BEGIN_DECLS GBinderRemoteReply* gbinder_remote_reply_ref( GBinderRemoteReply* reply); void gbinder_remote_reply_unref( GBinderRemoteReply* reply); void gbinder_remote_reply_init_reader( GBinderRemoteReply* reply, GBinderReader* reader); GBinderLocalReply* gbinder_remote_reply_copy_to_local( GBinderRemoteReply* reply) /* since 1.0.6 */ G_GNUC_WARN_UNUSED_RESULT; /* Convenience function to decode replies with just one data item */ gboolean gbinder_remote_reply_read_int32( GBinderRemoteReply* reply, gint32* value); gboolean gbinder_remote_reply_read_uint32( GBinderRemoteReply* reply, guint32* value); gboolean gbinder_remote_reply_read_int64( GBinderRemoteReply* reply, gint64* value); gboolean gbinder_remote_reply_read_uint64( GBinderRemoteReply* reply, guint64* value); const char* gbinder_remote_reply_read_string8( GBinderRemoteReply* reply); char* gbinder_remote_reply_read_string16( GBinderRemoteReply* reply) G_GNUC_WARN_UNUSED_RESULT; GBinderRemoteObject* gbinder_remote_reply_read_object( GBinderRemoteReply* reply) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /* GBINDER_REMOTE_REPLY_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_remote_request.h000066400000000000000000000070301526127616700222510ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_REQUEST_H #define GBINDER_REMOTE_REQUEST_H #include G_BEGIN_DECLS const char* gbinder_remote_request_interface( GBinderRemoteRequest* req); GBinderRemoteRequest* gbinder_remote_request_ref( GBinderRemoteRequest* req); void gbinder_remote_request_unref( GBinderRemoteRequest* req); void gbinder_remote_request_init_reader( GBinderRemoteRequest* req, GBinderReader* reader); pid_t gbinder_remote_request_sender_pid( GBinderRemoteRequest* req); /* since 1.0.2 */ uid_t gbinder_remote_request_sender_euid( GBinderRemoteRequest* req); /* since 1.0.2 */ GBinderLocalRequest* gbinder_remote_request_copy_to_local( GBinderRemoteRequest* req) /* since 1.0.6 */ G_GNUC_WARN_UNUSED_RESULT; void gbinder_remote_request_block( GBinderRemoteRequest* req); /* Since 1.0.20 */ void gbinder_remote_request_complete( GBinderRemoteRequest* req, GBinderLocalReply* reply, int status); /* Since 1.0.20 */ /* Convenience function to decode requests with just one data item */ gboolean gbinder_remote_request_read_int32( GBinderRemoteRequest* req, gint32* value); gboolean gbinder_remote_request_read_uint32( GBinderRemoteRequest* req, guint32* value); gboolean gbinder_remote_request_read_int64( GBinderRemoteRequest* req, gint64* value); gboolean gbinder_remote_request_read_uint64( GBinderRemoteRequest* req, guint64* value); const char* gbinder_remote_request_read_string8( GBinderRemoteRequest* req); char* gbinder_remote_request_read_string16( GBinderRemoteRequest* req) G_GNUC_WARN_UNUSED_RESULT; GBinderRemoteObject* gbinder_remote_request_read_object( GBinderRemoteRequest* self) G_GNUC_WARN_UNUSED_RESULT; G_END_DECLS #endif /* GBINDER_REMOTE_REQUEST_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_servicemanager.h000066400000000000000000000133651526127616700222110ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SERVICEMANAGER_H #define GBINDER_SERVICEMANAGER_H #include G_BEGIN_DECLS typedef void (*GBinderServiceManagerFunc)( GBinderServiceManager* sm, void* user_data); /* GBinderServiceManagerListFunc callback returns TRUE to keep the services * list, otherwise the caller will deallocate it. */ typedef gboolean (*GBinderServiceManagerListFunc)( GBinderServiceManager* sm, char** services, void* user_data); typedef void (*GBinderServiceManagerGetServiceFunc)( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data); typedef void (*GBinderServiceManagerAddServiceFunc)( GBinderServiceManager* sm, int status, void* user_data); typedef void (*GBinderServiceManagerRegistrationFunc)( GBinderServiceManager* sm, const char* name, void* user_data); GBinderServiceManager* gbinder_servicemanager_new( const char* dev) G_GNUC_WARN_UNUSED_RESULT; GBinderServiceManager* gbinder_servicemanager_new2( const char* dev, const char* sm_protocol, const char* rpc_protocol) /* Since 1.1.20 */ G_GNUC_WARN_UNUSED_RESULT; GBinderServiceManager* gbinder_defaultservicemanager_new( const char* dev) G_DEPRECATED_FOR(gbinder_servicemanager_new); GBinderServiceManager* gbinder_hwservicemanager_new( const char* dev) G_DEPRECATED_FOR(gbinder_servicemanager_new); GBinderLocalObject* gbinder_servicemanager_new_local_object( GBinderServiceManager* sm, const char* iface, GBinderLocalTransactFunc handler, void* user_data) G_GNUC_WARN_UNUSED_RESULT; GBinderLocalObject* gbinder_servicemanager_new_local_object2( GBinderServiceManager* sm, const char* const* ifaces, GBinderLocalTransactFunc handler, void* user_data) /* Since 1.0.29 */ G_GNUC_WARN_UNUSED_RESULT; GBinderServiceManager* gbinder_servicemanager_ref( GBinderServiceManager* sm); void gbinder_servicemanager_unref( GBinderServiceManager* sm); const char* gbinder_servicemanager_device( GBinderServiceManager* sm); /* Since 1.1.14 */ gboolean gbinder_servicemanager_is_present( GBinderServiceManager* sm); /* Since 1.0.25 */ gboolean gbinder_servicemanager_wait( GBinderServiceManager* sm, long max_wait_ms); /* Since 1.0.25 */ gulong gbinder_servicemanager_list( GBinderServiceManager* sm, GBinderServiceManagerListFunc func, void* user_data); char** gbinder_servicemanager_list_sync( GBinderServiceManager* sm) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC; gulong gbinder_servicemanager_get_service( GBinderServiceManager* sm, const char* name, GBinderServiceManagerGetServiceFunc func, void* user_data); GBinderRemoteObject* /* autoreleased */ gbinder_servicemanager_get_service_sync( GBinderServiceManager* sm, const char* name, int* status); gulong gbinder_servicemanager_add_service( GBinderServiceManager* sm, const char* name, GBinderLocalObject* obj, GBinderServiceManagerAddServiceFunc func, void* user_data); int gbinder_servicemanager_add_service_sync( GBinderServiceManager* sm, const char* name, GBinderLocalObject* obj); void gbinder_servicemanager_cancel( GBinderServiceManager* sm, gulong id); gulong gbinder_servicemanager_add_presence_handler( GBinderServiceManager* sm, GBinderServiceManagerFunc func, void* user_data); /* Since 1.0.25 */ gulong gbinder_servicemanager_add_registration_handler( GBinderServiceManager* sm, const char* name, GBinderServiceManagerRegistrationFunc func, void* user_data); /* Since 1.0.13 */ void gbinder_servicemanager_remove_handler( GBinderServiceManager* sm, gulong id); /* Since 1.0.13 */ void gbinder_servicemanager_remove_handlers( GBinderServiceManager* sm, gulong* ids, guint count); /* Since 1.0.25 */ #define gbinder_servicemanager_remove_all_handlers(sm,ids) \ gbinder_servicemanager_remove_handlers(sm, ids, G_N_ELEMENTS(ids)) G_END_DECLS #endif /* GBINDER_SERVICEMANAGER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_servicename.h000066400000000000000000000043721526127616700215150ustar00rootroot00000000000000/* * Copyright (C) 2019 Jolla Ltd. * Copyright (C) 2019 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SERVICENAME_H #define GBINDER_SERVICENAME_H #include "gbinder_types.h" G_BEGIN_DECLS /* Since 1.0.26 */ struct gbinder_servicename { const char* name; }; GBinderServiceName* gbinder_servicename_new( GBinderServiceManager* sm, GBinderLocalObject* object, const char* name); GBinderServiceName* gbinder_servicename_ref( GBinderServiceName* name); void gbinder_servicename_unref( GBinderServiceName* name); G_END_DECLS #endif /* GBINDER_SERVICENAME_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_types.h000066400000000000000000000146751526127616700203670ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_TYPES_H #define GBINDER_TYPES_H #include G_BEGIN_DECLS #define GBINDER_LOG_MODULE gbinder_log /* * Terminology: * * 1. RemoteObject is the one we sent requests to. * 2. LocalObjects may receive incoming transactions (and reply to them) * 3. We must have a RemoteObject to initiate a transaction. * We send LocalRequest and receive RemoteReply: * * LocalObject --- (LocalRequest) --> Client(RemoteObject) * LocalObject <-- (RemoteReply) -- RemoteObject * * 4. LocalObject knows caller's pid (and therefore credentials) * * LocalObject <-- (RemoteRequest) --- (pid) * LocalObject --- (LocalReply) --> (pid) * * 5. Writer prepares the data for LocalRequest and LocalReply * 6. Reader parses the data coming with RemoteRequest and RemoteReply */ typedef struct gbinder_bridge GBinderBridge; /* Since 1.1.5 */ typedef struct gbinder_buffer GBinderBuffer; typedef struct gbinder_client GBinderClient; typedef struct gbinder_fmq GBinderFmq; /* Since 1.1.14 */ typedef struct gbinder_ipc GBinderIpc; typedef struct gbinder_local_object GBinderLocalObject; typedef struct gbinder_local_reply GBinderLocalReply; typedef struct gbinder_local_request GBinderLocalRequest; typedef struct gbinder_reader GBinderReader; typedef struct gbinder_remote_object GBinderRemoteObject; typedef struct gbinder_remote_reply GBinderRemoteReply; typedef struct gbinder_remote_request GBinderRemoteRequest; typedef struct gbinder_servicename GBinderServiceName; typedef struct gbinder_servicemanager GBinderServiceManager; typedef struct gbinder_writer GBinderWriter; typedef struct gbinder_parent GBinderParent; /* Basic HIDL types */ #define GBINDER_ALIGNED(x) __attribute__ ((aligned(x))) typedef struct gbinder_hidl_vec { union { guint64 value; const void* ptr; } data; guint32 count; guint8 owns_buffer; guint8 pad[3]; } GBinderHidlVec; #define GBINDER_HIDL_VEC_BUFFER_OFFSET (0) G_STATIC_ASSERT(sizeof(GBinderHidlVec) == 16); typedef struct gbinder_hidl_string { union { guint64 value; const char* str; } data; guint32 len; guint8 owns_buffer; guint8 pad[3]; } GBinderHidlString; #define GBINDER_HIDL_STRING_BUFFER_OFFSET (0) G_STATIC_ASSERT(sizeof(GBinderHidlString) == 16); typedef struct gbinder_fds { guint32 version GBINDER_ALIGNED(4); guint32 num_fds GBINDER_ALIGNED(4); guint32 num_ints GBINDER_ALIGNED(4); } GBINDER_ALIGNED(4) GBinderFds; /* Since 1.1.4 */ /* Actual fds immediately follow GBinderFds: */ #define gbinder_fds_get_fd(fds,i) (((const int*)((fds) + 1))[i]) #define GBINDER_HIDL_FDS_VERSION (12) G_STATIC_ASSERT(sizeof(GBinderFds) == GBINDER_HIDL_FDS_VERSION); typedef struct gbinder_hidl_handle { union { guint64 value; const GBinderFds* fds; } data; guint8 owns_handle; guint8 pad[7]; } GBinderHidlHandle; /* Since 1.1.4 */ #define GBINDER_HIDL_HANDLE_VALUE_OFFSET (0) G_STATIC_ASSERT(sizeof(GBinderHidlHandle) == 16); typedef struct gbinder_hidl_memory { union { guint64 value; const GBinderFds* fds; } data; guint8 owns_buffer; guint8 pad[7]; guint64 size; GBinderHidlString name; } GBinderHidlMemory; /* Since 1.1.4 */ #define GBINDER_HIDL_MEMORY_PTR_OFFSET (0) #define GBINDER_HIDL_MEMORY_NAME_OFFSET (24) G_STATIC_ASSERT(sizeof(GBinderHidlMemory) == 40); /* * Each RPC call is identified by the interface name returned * by gbinder_remote_request_interface() the transaction code. * Transaction code itself is not unique. * * The value returned from GBinderLocalTransactFunc callback will * be ignored for one-way transactions. If GBINDER_TX_FLAG_ONEWAY * is passed in, the callback may and should return NULL and that * won't be interpreted as an error. */ typedef GBinderLocalReply* (*GBinderLocalTransactFunc)( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data); #define GBINDER_TX_FLAG_ONEWAY (0x01) typedef enum gbinder_status { GBINDER_STATUS_OK = 0, GBINDER_STATUS_FAILED, GBINDER_STATUS_DEAD_OBJECT } GBINDER_STATUS; typedef enum gbinder_stability_level { GBINDER_STABILITY_UNDECLARED = 0, GBINDER_STABILITY_VENDOR = 0x03, GBINDER_STABILITY_SYSTEM = 0x0c, GBINDER_STABILITY_VINTF = 0x3f } GBINDER_STABILITY_LEVEL; /* Since 1.1.40 */ #define GBINDER_FOURCC(c1,c2,c3,c4) \ (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4)) #define GBINDER_FIRST_CALL_TRANSACTION (0x00000001) /* Default binder devices */ #define GBINDER_DEFAULT_BINDER "/dev/binder" #define GBINDER_DEFAULT_HWBINDER "/dev/hwbinder" extern GLogModule GBINDER_LOG_MODULE; G_END_DECLS #endif /* GBINDER_TYPES_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/include/gbinder_writer.h000066400000000000000000000241361526127616700205300ustar00rootroot00000000000000/* * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_WRITER_H #define GBINDER_WRITER_H #include G_BEGIN_DECLS /* * Writers are initialized by LocalRequest and LocalReply objects. * Note that writers directly update the objects which initialized * them but don't reference those object. The caller must make sure * that objects outlive their writers. * * Writers are normally allocated on stack. */ struct gbinder_writer { gconstpointer d[4]; }; struct gbinder_parent { guint32 index; guint32 offset; }; /* * Note that gbinder_writer_append_struct() doesn't copy the data, it writes * buffer objects pointing to whatever was passed in. The caller must make * sure that those pointers outlive the transaction. That's most commonly * done with by using gbinder_writer_malloc() and friends for allocating * memory for the transaction. * * Below is an example of initializing GBinderWriterType which can then * be passed to gbinder_writer_append_struct(). Fields have to be listed * in the order in which they appear in the structure. * * typedef struct data { * int x; * } Data; * * typedef struct data2 { * int y; * GBinderHidlString str; * GBinderHidlVec vec; // vec * } Data2; * * static const GBinderWriterType data_t = { * GBINDER_WRITER_STRUCT_NAME_AND_SIZE(Data), NULL * }; * * static const GBinderWriterField data2_f[] = { * GBINDER_WRITER_FIELD_HIDL_STRING(Data2,str), * GBINDER_WRITER_FIELD_HIDL_VEC(Data2, vec, &data_t), * GBINDER_WRITER_FIELD_END() * }; * * static const GBinderWriterType data2_t = { * GBINDER_WRITER_STRUCT_NAME_AND_SIZE(Data2), data2_f * }; */ typedef struct gbinder_writer_type { const char* name; gsize size; const struct gbinder_writer_field* fields; } GBinderWriterType; /* Since 1.1.27 */ typedef struct gbinder_writer_field { const char* name; gsize offset; const GBinderWriterType* type; void (*write_buf)(GBinderWriter* writer, const void* ptr, const struct gbinder_writer_field* field, const GBinderParent* parent); gpointer reserved; } GBinderWriterField; /* Since 1.1.27 */ #define GBINDER_WRITER_STRUCT_NAME_AND_SIZE(type) \ #type, sizeof(type) #define GBINDER_WRITER_FIELD_NAME_AND_OFFSET(type,field) \ #type "." #field, G_STRUCT_OFFSET(type,field) #define GBINDER_WRITER_FIELD_POINTER(type,field,field_type) { \ GBINDER_WRITER_FIELD_NAME_AND_OFFSET(type,field), field_type, NULL, NULL } #define GBINDER_WRITER_FIELD_HIDL_VEC(type,field,elem) { \ GBINDER_WRITER_FIELD_NAME_AND_OFFSET(type,field), elem, \ gbinder_writer_field_hidl_vec_write_buf, NULL } #define GBINDER_WRITER_FIELD_HIDL_VEC_INT32(type,field) \ GBINDER_WRITER_FIELD_HIDL_VEC(type,field, &gbinder_writer_type_int32) #define GBINDER_WRITER_FIELD_HIDL_VEC_BYTE(type,field) \ GBINDER_WRITER_FIELD_HIDL_VEC(type,field, &gbinder_writer_type_byte) #define GBINDER_WRITER_FIELD_HIDL_VEC_STRING(type,field) \ GBINDER_WRITER_FIELD_HIDL_VEC(type,field, &gbinder_writer_type_hidl_string) #define GBINDER_WRITER_FIELD_HIDL_STRING(type,field) { \ GBINDER_WRITER_FIELD_NAME_AND_OFFSET(type,field), NULL, \ gbinder_writer_field_hidl_string_write_buf, NULL } #define GBINDER_WRITER_FIELD_END() { NULL, 0, NULL, NULL, NULL } extern const GBinderWriterType gbinder_writer_type_byte; /* Since 1.1.27 */ extern const GBinderWriterType gbinder_writer_type_int32; /* Since 1.1.27 */ extern const GBinderWriterType gbinder_writer_type_hidl_string; /* 1.1.27 */ void gbinder_writer_append_struct( GBinderWriter* writer, const void* ptr, const GBinderWriterType* type, const GBinderParent* parent); /* Since 1.1.27 */ void gbinder_writer_append_struct_vec( GBinderWriter* writer, const void* ptr, guint count, const GBinderWriterType* type); /* Since 1.1.29 */ void gbinder_writer_field_hidl_vec_write_buf( GBinderWriter* writer, const void* ptr, const GBinderWriterField* field, const GBinderParent* parent); /* Since 1.1.27 */ void gbinder_writer_field_hidl_string_write_buf( GBinderWriter* writer, const void* ptr, const GBinderWriterField* field, const GBinderParent* parent); /* Since 1.1.27 */ void gbinder_writer_append_int8( GBinderWriter* writer, guint8 value); /* Since 1.1.15 */ void gbinder_writer_append_int16( GBinderWriter* writer, guint16 value); /* Since 1.1.15 */ void gbinder_writer_append_int32( GBinderWriter* writer, guint32 value); void gbinder_writer_append_int64( GBinderWriter* writer, guint64 value); void gbinder_writer_append_float( GBinderWriter* writer, gfloat value); void gbinder_writer_append_double( GBinderWriter* writer, gdouble value); void gbinder_writer_append_string16( GBinderWriter* writer, const char* utf8); void gbinder_writer_append_string16_len( GBinderWriter* writer, const char* utf8, gssize num_bytes); void gbinder_writer_append_string16_utf16( GBinderWriter* writer, const gunichar2* utf16, gssize length); /* Since 1.0.17 */ void gbinder_writer_append_string8( GBinderWriter* writer, const char* str); void gbinder_writer_append_string8_len( GBinderWriter* writer, const char* str, gsize len); void gbinder_writer_append_bool( GBinderWriter* writer, gboolean value); void gbinder_writer_append_bytes( GBinderWriter* writer, const void* data, gsize size); void gbinder_writer_append_fd( GBinderWriter* writer, int fd); /* Since 1.0.18 */ void gbinder_writer_append_fds( GBinderWriter* writer, const GBinderFds* fds, const GBinderParent* parent); /* Since 1.1.43 */ guint gbinder_writer_append_buffer_object_with_parent( GBinderWriter* writer, const void* buf, gsize len, const GBinderParent* parent); guint gbinder_writer_append_buffer_object( GBinderWriter* writer, const void* buf, gsize len); void gbinder_writer_append_parcelable( GBinderWriter* writer, const void* buf, gsize len); /* Since 1.1.19 */ #define gbinder_writer_append_null_parcelable(writer) \ gbinder_writer_append_parcelable(writer, NULL, 0) /* Since 1.1.47 */ void gbinder_writer_start_parcelable( GBinderWriter* writer, GBinderWriter* parcelable); /* Since 1.1.47 */ void gbinder_writer_finish_parcelable( GBinderWriter* parcelable); /* Since 1.1.47 */ void gbinder_writer_append_hidl_vec( GBinderWriter* writer, const void* base, guint count, guint elemsize); /* Since 1.0.8 */ void gbinder_writer_append_hidl_string( GBinderWriter* writer, const char* str); void gbinder_writer_append_hidl_string_copy( GBinderWriter* writer, const char* str); /* Since 1.1.13 */ void gbinder_writer_append_hidl_string_vec( GBinderWriter* writer, const char* data[], gssize count); void gbinder_writer_append_local_object( GBinderWriter* writer, GBinderLocalObject* obj); void gbinder_writer_append_remote_object( GBinderWriter* writer, GBinderRemoteObject* obj); void gbinder_writer_append_byte_array( GBinderWriter* writer, const void* byte_array, gint32 len); /* Since 1.0.12 */ void gbinder_writer_append_fmq_descriptor( GBinderWriter* writer, const GBinderFmq* queue); /* since 1.1.14 */ const void* gbinder_writer_get_data( GBinderWriter* writer, gsize* size); /* Since 1.1.14 */ gsize gbinder_writer_bytes_written( GBinderWriter* writer); /* Since 1.0.21 */ void gbinder_writer_overwrite_int32( GBinderWriter* writer, gsize offset, gint32 value); /* Since 1.0.21 */ /* Note: memory allocated by GBinderWriter is owned by GBinderWriter */ void* gbinder_writer_malloc( GBinderWriter* writer, gsize size); /* Since 1.0.19 */ void* gbinder_writer_malloc0( GBinderWriter* writer, gsize size); /* Since 1.0.19 */ #define gbinder_writer_new(writer,type) \ ((type*) gbinder_writer_malloc(writer, sizeof(type))) #define gbinder_writer_new0(writer,type) \ ((type*) gbinder_writer_malloc0(writer, sizeof(type))) void gbinder_writer_add_cleanup( GBinderWriter* writer, GDestroyNotify destroy, gpointer data); /* Since 1.0.19 */ void* gbinder_writer_memdup( GBinderWriter* writer, const void* buf, gsize size); /* Since 1.0.19 */ char* gbinder_writer_strdup( GBinderWriter* writer, const char* str); /* Since 1.1.13 */ G_END_DECLS #endif /* GBINDER_WRITER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/libgbinder.pc.in000066400000000000000000000003601526127616700167510ustar00rootroot00000000000000name=gbinder libdir=@libdir@ includedir=/usr/include Name: libgbinder Description: Binder client library Version: @version@ Requires.private: glib-2.0 libglibutil Libs: -L${libdir} -l${name} Cflags: -I${includedir} -I${includedir}/${name} libgbinder-1.1.54/misc/000077500000000000000000000000001526127616700146535ustar00rootroot00000000000000libgbinder-1.1.54/misc/coverity_model.c000066400000000000000000000002371526127616700200450ustar00rootroot00000000000000typedef struct gbinder_remote_request GBinderRemoteRequest; void gbinder_remote_request_unref( GBinderRemoteRequest* req) { __coverity_free__(req); } libgbinder-1.1.54/rpm/000077500000000000000000000000001526127616700145165ustar00rootroot00000000000000libgbinder-1.1.54/rpm/libgbinder.spec000066400000000000000000000042521526127616700174760ustar00rootroot00000000000000Name: libgbinder Version: 1.1.54 Release: 0 Summary: Binder client library License: BSD URL: https://github.com/mer-hybris/libgbinder Source: %{name}-%{version}.tar.bz2 %define glib_version 2.32 %define libglibutil_version 1.0.83 BuildRequires: pkgconfig(glib-2.0) >= %{glib_version} BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version} BuildRequires: pkgconfig BuildRequires: bison BuildRequires: flex # license macro requires rpm >= 4.11 BuildRequires: pkgconfig(rpm) %define license_support %(pkg-config --exists 'rpm >= 4.11'; echo $?) # make_build macro appeared in rpm 4.12 %{!?make_build:%define make_build make %{_smp_mflags}} # openSUSE workaround %if 0%{?suse_version} > 0 %define libname %{name}%{so_ver} %define so_ver %(echo %{version} | cut -d. -f1) %description C interfaces for Android binder %package -n %{libname} Summary: Runtime library for %{name} Provides: %{name} = %{version} %else %define libname %{name} %endif Requires: glib2 >= %{glib_version} Requires: libglibutil >= %{libglibutil_version} Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig %description -n %{libname} C interfaces for Android binder %package devel Summary: Development library for %{name} Requires: %{libname} = %{version} Requires: pkgconfig(glib-2.0) >= %{glib_version} %description devel This package contains the development library for %{name}. %prep %setup -q %build %make_build LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig %make_build -C tools KEEP_SYMBOLS=1 release %install make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev make -C tools DESTDIR=%{buildroot} install %check make -C unit test %post -n %{libname} -p /sbin/ldconfig %postun -n %{libname} -p /sbin/ldconfig %files -n %{libname} %{_libdir}/%{name}.so.* %if %{license_support} == 0 %license LICENSE %endif %files devel %dir %{_includedir}/gbinder %{_libdir}/pkgconfig/*.pc %{_libdir}/%{name}.so %{_includedir}/gbinder/*.h # Tools %package tools Summary: Binder tools %if 0%{?suse_version} == 0 Requires: %{libname} >= %{version} %endif %description tools Binder command line utilities %files tools %{_bindir}/binder-bridge %{_bindir}/binder-list %{_bindir}/binder-ping %{_bindir}/binder-call libgbinder-1.1.54/src/000077500000000000000000000000001526127616700145075ustar00rootroot00000000000000libgbinder-1.1.54/src/binder.h000066400000000000000000000146601526127616700161320ustar00rootroot00000000000000/**************************************************************************** **************************************************************************** *** *** This header was automatically generated from a Linux kernel header *** of the same name, to make information necessary for userspace to *** call into the kernel available to libc. It contains only constants, *** structures, and macros generated from the original header, and thus, *** contains no copyrightable information. *** *** To edit the content of this header, modify the corresponding *** source file (e.g. under external/kernel-headers/original/) then *** run bionic/libc/kernel/tools/update_all.py *** *** Any manual change here will be lost the next time this script will *** be run. You've been warned! *** **************************************************************************** ****************************************************************************/ #ifndef _UAPI_LINUX_BINDER_H #define _UAPI_LINUX_BINDER_H #include #include #define B_PACK_CHARS(c1,c2,c3,c4) ((((c1) << 24)) | (((c2) << 16)) | (((c3) << 8)) | (c4)) #define B_TYPE_LARGE 0x85 enum { BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE), BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE), BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE), BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE), BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE), BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE), BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), }; enum { FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, }; #ifdef BINDER_IPC_32BIT typedef __u32 binder_size_t; typedef __u32 binder_uintptr_t; #else typedef __u64 binder_size_t; typedef __u64 binder_uintptr_t; #endif struct binder_object_header { __u32 type; }; struct flat_binder_object { struct binder_object_header hdr; __u32 flags; union { binder_uintptr_t binder; __u32 handle; }; binder_uintptr_t cookie; }; struct binder_fd_object { struct binder_object_header hdr; __u32 pad_flags; union { binder_uintptr_t pad_binder; __u32 fd; }; binder_uintptr_t cookie; }; struct binder_buffer_object { struct binder_object_header hdr; __u32 flags; binder_uintptr_t buffer; binder_size_t length; binder_size_t parent; binder_size_t parent_offset; }; enum { BINDER_BUFFER_FLAG_HAS_PARENT = 0x01, }; struct binder_fd_array_object { struct binder_object_header hdr; __u32 pad; binder_size_t num_fds; binder_size_t parent; binder_size_t parent_offset; }; struct binder_write_read { binder_size_t write_size; binder_size_t write_consumed; binder_uintptr_t write_buffer; binder_size_t read_size; binder_size_t read_consumed; binder_uintptr_t read_buffer; }; struct binder_version { __s32 protocol_version; }; #ifdef BINDER_IPC_32BIT #define BINDER_CURRENT_PROTOCOL_VERSION 7 #else #define BINDER_CURRENT_PROTOCOL_VERSION 8 #endif struct binder_node_debug_info { binder_uintptr_t ptr; binder_uintptr_t cookie; __u32 has_strong_ref; __u32 has_weak_ref; }; #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32) #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) enum transaction_flags { TF_ONE_WAY = 0x01, TF_ROOT_OBJECT = 0x04, TF_STATUS_CODE = 0x08, TF_ACCEPT_FDS = 0x10, }; struct binder_transaction_data { union { __u32 handle; binder_uintptr_t ptr; } target; binder_uintptr_t cookie; __u32 code; __u32 flags; pid_t sender_pid; uid_t sender_euid; binder_size_t data_size; binder_size_t offsets_size; union { struct { binder_uintptr_t buffer; binder_uintptr_t offsets; } ptr; __u8 buf[8]; } data; }; struct binder_transaction_data_sg { struct binder_transaction_data transaction_data; binder_size_t buffers_size; }; struct binder_ptr_cookie { binder_uintptr_t ptr; binder_uintptr_t cookie; }; struct binder_handle_cookie { __u32 handle; binder_uintptr_t cookie; } __attribute__((packed)); struct binder_pri_desc { __s32 priority; __u32 desc; }; struct binder_pri_ptr_cookie { __s32 priority; binder_uintptr_t ptr; binder_uintptr_t cookie; }; enum binder_driver_return_protocol { BR_ERROR = _IOR('r', 0, __s32), BR_OK = _IO('r', 1), BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), BR_REPLY = _IOR('r', 3, struct binder_transaction_data), BR_ACQUIRE_RESULT = _IOR('r', 4, __s32), BR_DEAD_REPLY = _IO('r', 5), BR_TRANSACTION_COMPLETE = _IO('r', 6), BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie), BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie), BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie), BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie), BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie), BR_NOOP = _IO('r', 12), BR_SPAWN_LOOPER = _IO('r', 13), BR_FINISHED = _IO('r', 14), BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t), BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t), BR_FAILED_REPLY = _IO('r', 17), }; enum binder_driver_command_protocol { BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', 1, struct binder_transaction_data), BC_ACQUIRE_RESULT = _IOW('c', 2, __s32), BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t), BC_INCREFS = _IOW('c', 4, __u32), BC_ACQUIRE = _IOW('c', 5, __u32), BC_RELEASE = _IOW('c', 6, __u32), BC_DECREFS = _IOW('c', 7, __u32), BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie), BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie), BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc), BC_REGISTER_LOOPER = _IO('c', 11), BC_ENTER_LOOPER = _IO('c', 12), BC_EXIT_LOOPER = _IO('c', 13), BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_handle_cookie), BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_handle_cookie), BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t), BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg), BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg), }; #endif libgbinder-1.1.54/src/gbinder_bridge.c000066400000000000000000000227451526127616700176130ustar00rootroot00000000000000/* * Copyright (C) 2021-2026 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd. * Copyright (C) 2021 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_bridge.h" #include "gbinder_client_p.h" #include "gbinder_log.h" #include "gbinder_proxy_object.h" #include "gbinder_remote_object_p.h" #include "gbinder_servicemanager_aidl.h" #include "gbinder_servicemanager_p.h" #include "gbinder_servicename.h" #include #include #include typedef struct gbinder_bridge_interface { GBinderBridge* bridge; char* iface; char* fqname; char* src_name; char* dest_name; gulong dest_watch_id; gulong dest_death_id; GBinderRemoteObject* dest_obj; GBinderServiceName* src_service; GBinderProxyObject* proxy; } GBinderBridgeInterface; struct gbinder_bridge { GBinderBridgeInterface** ifaces; GBinderServiceManager* src; GBinderServiceManager* dest; }; /*==========================================================================* * Implementation *==========================================================================*/ static gboolean gbinder_bridge_aidl_manager( GBinderServiceManager* sm) { return g_type_is_a(G_OBJECT_TYPE(sm), GBINDER_TYPE_SERVICEMANAGER_AIDL); } static gboolean gbinder_bridge_manager_compatible( GBinderServiceManager* src, GBinderServiceManager* dest) { const gboolean src_aidl = gbinder_bridge_aidl_manager(src); const gboolean dest_aidl = gbinder_bridge_aidl_manager(dest); if (src_aidl != dest_aidl) { GWARN("Incompatible service manager types (%s -> %s)", G_OBJECT_TYPE_NAME(src), G_OBJECT_TYPE_NAME(dest)); return FALSE; } return TRUE; } static void gbinder_bridge_dest_drop_remote_object( GBinderBridgeInterface* bi) { if (bi->dest_obj) { GDEBUG("Detached from %s", bi->fqname); gbinder_remote_object_remove_handler(bi->dest_obj, bi->dest_death_id); gbinder_remote_object_unref(bi->dest_obj); bi->dest_death_id = 0; bi->dest_obj = NULL; } } static void gbinder_bridge_interface_deactivate( GBinderBridgeInterface* bi) { gbinder_bridge_dest_drop_remote_object(bi); if (bi->proxy) { gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(bi->proxy)); bi->proxy = NULL; } if (bi->src_service) { gbinder_servicename_unref(bi->src_service); bi->src_service = NULL; } } static void gbinder_bridge_interface_free( GBinderBridgeInterface* bi) { GBinderBridge* bridge = bi->bridge; gbinder_bridge_interface_deactivate(bi); gbinder_servicemanager_remove_handler(bridge->dest, bi->dest_watch_id); g_free(bi->iface); g_free(bi->fqname); g_free(bi->src_name); g_free(bi->dest_name); gutil_slice_free(bi); } static void gbinder_bridge_dest_death_proc( GBinderRemoteObject* obj, void* user_data) { GBinderBridgeInterface* bi = user_data; GDEBUG("%s has died", bi->fqname); gbinder_bridge_interface_deactivate(bi); } static void gbinder_bridge_interface_activate( GBinderBridgeInterface* bi) { GBinderBridge* bridge = bi->bridge; GBinderServiceManager* src = bridge->src; GBinderServiceManager* dest = bridge->dest; if (bi->dest_obj && bi->dest_obj->dead) { gbinder_bridge_dest_drop_remote_object(bi); } if (!bi->dest_obj) { bi->dest_obj = gbinder_servicemanager_get_service_sync(dest, bi->fqname, NULL); if (bi->dest_obj) { GDEBUG("Attached to %s", bi->fqname); gbinder_remote_object_ref(bi->dest_obj); bi->dest_death_id = gbinder_remote_object_add_death_handler (bi->dest_obj, gbinder_bridge_dest_death_proc, bi); } } if (bi->dest_obj && !bi->proxy) { bi->proxy = gbinder_proxy_object_new(gbinder_servicemanager_ipc(src), bi->dest_obj); } if (bi->proxy && !bi->src_service) { bi->src_service = gbinder_servicename_new(src, GBINDER_LOCAL_OBJECT(bi->proxy), bi->src_name); } } static void gbinder_bridge_dest_registration_proc( GBinderServiceManager* sm, const char* name, void* user_data) { GBinderBridgeInterface* bi = user_data; if (!g_strcmp0(name, bi->fqname)) { GDEBUG("%s has been registered", bi->fqname); gbinder_bridge_interface_activate(bi); } } static GBinderBridgeInterface* gbinder_bridge_interface_new( GBinderBridge* self, const char* src_name, const char* dest_name, const char* iface) { GBinderBridgeInterface* bi = g_slice_new0(GBinderBridgeInterface); bi->bridge = self; if (iface) { bi->iface = g_strdup(iface); bi->fqname = g_strconcat(iface, "/", dest_name, NULL); } else { bi->fqname = g_strdup(dest_name); } bi->src_name = g_strdup(src_name); bi->dest_name = g_strdup(dest_name); bi->dest_watch_id = gbinder_servicemanager_add_registration_handler (self->dest, bi->fqname, gbinder_bridge_dest_registration_proc, bi); /* Try to activate at startup */ gbinder_bridge_interface_activate(bi); return bi; } /*==========================================================================* * Interface *==========================================================================*/ GBinderBridge* gbinder_bridge_new( const char* name, const char* const* ifaces, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.5 */ { if (gutil_strv_length((const GStrV*)ifaces) > 0) { return gbinder_bridge_new2(name, NULL, ifaces, src, dest); } else { return gbinder_bridge_new3(name, NULL, src, dest); } } GBinderBridge* gbinder_bridge_new2( const char* src_name, const char* dest_name, const char* const* ifaces, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.6 */ { const guint n = gutil_strv_length((const GStrV*)ifaces); if (!src_name) { src_name = dest_name; } else if (!dest_name) { dest_name = src_name; } if (G_LIKELY(src_name) && G_LIKELY(n) && G_LIKELY(src) && G_LIKELY(dest) && G_LIKELY(gbinder_bridge_manager_compatible(src, dest))) { GBinderBridge* self = g_slice_new0(GBinderBridge); guint i; self->src = gbinder_servicemanager_ref(src); self->dest = gbinder_servicemanager_ref(dest); self->ifaces = g_new(GBinderBridgeInterface*, n + 1); for (i = 0; i < n; i++) { self->ifaces[i] = gbinder_bridge_interface_new(self, src_name, dest_name, ifaces[i]); } self->ifaces[i] = NULL; return self; } return NULL; } GBinderBridge* gbinder_bridge_new3( const char* src_name, const char* dest_name, GBinderServiceManager* src, GBinderServiceManager* dest) /* Since 1.1.44 */ { if (!src_name) { src_name = dest_name; } else if (!dest_name) { dest_name = src_name; } if (G_LIKELY(src_name) && G_LIKELY(dest_name) && G_LIKELY(src) && G_LIKELY(dest) && G_LIKELY(gbinder_bridge_manager_compatible(src, dest))) { GBinderBridge* self = g_slice_new0(GBinderBridge); self->src = gbinder_servicemanager_ref(src); self->dest = gbinder_servicemanager_ref(dest); self->ifaces = g_new(GBinderBridgeInterface*, 2); self->ifaces[0] = gbinder_bridge_interface_new(self, src_name, dest_name, NULL); self->ifaces[1] = NULL; return self; } return NULL; } void gbinder_bridge_free( GBinderBridge* self) { if (G_LIKELY(self)) { GBinderBridgeInterface** bi = self->ifaces; while (*bi) { gbinder_bridge_interface_free(*bi); bi++; } gbinder_servicemanager_unref(self->src); gbinder_servicemanager_unref(self->dest); g_free(self->ifaces); gutil_slice_free(self); } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_buffer.c000066400000000000000000000167161526127616700176310ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * 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_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_log.h" #include struct gbinder_buffer_contents { gint refcount; void* buffer; gsize size; void** objects; GBinderDriver* driver; }; typedef struct gbinder_buffer_priv { GBinderBuffer pub; GBinderBufferContents* contents; } GBinderBufferPriv; static inline GBinderBufferPriv* gbinder_buffer_cast(GBinderBuffer* buf) { return G_CAST(buf, GBinderBufferPriv, pub); } /*==========================================================================* * GBinderBufferContents *==========================================================================*/ static GBinderBufferContents* gbinder_buffer_contents_new( GBinderDriver* driver, void* buffer, gsize size, void** objects) { GBinderBufferContents* self = g_slice_new0(GBinderBufferContents); g_atomic_int_set(&self->refcount, 1); self->buffer = buffer; self->size = size; self->objects = objects; self->driver = gbinder_driver_ref(driver); return self; } static void gbinder_buffer_contents_free( GBinderBufferContents* self) { if (self->objects) { gbinder_driver_close_fds(self->driver, self->objects, ((guint8*)self->buffer) + self->size); g_free(self->objects); } gbinder_driver_free_buffer(self->driver, self->buffer); gbinder_driver_unref(self->driver); g_slice_free(GBinderBufferContents, self); } GBinderBufferContents* gbinder_buffer_contents_ref( GBinderBufferContents* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return self; } void gbinder_buffer_contents_unref( GBinderBufferContents* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_buffer_contents_free(self); } } } /*==========================================================================* * GBinderBufferContentsList * It's actually a GSList containing GBinderBufferContents refs. *==========================================================================*/ GBinderBufferContentsList* gbinder_buffer_contents_list_add( GBinderBufferContentsList* list, GBinderBufferContents* contents) { /* Prepend rather than append for better efficiency */ return contents ? (GBinderBufferContentsList*) g_slist_prepend((GSList*) list, gbinder_buffer_contents_ref(contents)) : list; } GBinderBufferContentsList* gbinder_buffer_contents_list_dup( GBinderBufferContentsList* list) { GSList* out = NULL; if (list) { GSList* l = (GSList*) list; /* The order gets reversed but it doesn't matter */ while (l) { out = g_slist_prepend(out, gbinder_buffer_contents_ref(l->data)); l = l->next; } } return (GBinderBufferContentsList*) out; } void gbinder_buffer_contents_list_free( GBinderBufferContentsList* list) { g_slist_free_full((GSList*) list, (GDestroyNotify) gbinder_buffer_contents_unref); } /*==========================================================================* * GBinderBuffer *==========================================================================*/ static GBinderBuffer* gbinder_buffer_alloc( GBinderBufferContents* contents, void* data, gsize size) { GBinderBufferPriv* priv = g_slice_new0(GBinderBufferPriv); GBinderBuffer* self = &priv->pub; priv->contents = contents; self->data = data; self->size = size; return self; } void gbinder_buffer_free( GBinderBuffer* self) { if (G_LIKELY(self)) { GBinderBufferPriv* priv = gbinder_buffer_cast(self); gbinder_buffer_contents_unref(priv->contents); g_slice_free(GBinderBufferPriv, priv); } } GBinderBuffer* gbinder_buffer_new( GBinderDriver* driver, void* data, gsize size, void** objects) { return gbinder_buffer_alloc((driver && data) ? gbinder_buffer_contents_new(driver, data, size, objects) : NULL, data, size); } GBinderBuffer* gbinder_buffer_new_with_parent( GBinderBuffer* parent, void* data, gsize size) { return gbinder_buffer_alloc(parent ? gbinder_buffer_contents_ref(gbinder_buffer_contents(parent)) : NULL, data, size); } gconstpointer gbinder_buffer_data( GBinderBuffer* self, gsize* size) { GBinderBufferContents* contents = gbinder_buffer_contents(self); if (G_LIKELY(contents)) { if (size) { *size = contents->size; } return contents->buffer; } else { if (size) { *size = 0; } return NULL; } } GBinderDriver* gbinder_buffer_driver( GBinderBuffer* self) { if (G_LIKELY(self)) { GBinderBufferPriv* priv = gbinder_buffer_cast(self); if (priv->contents) { return priv->contents->driver; } } return NULL; } const GBinderIo* gbinder_buffer_io( GBinderBuffer* buf) { GBinderDriver* driver = gbinder_buffer_driver(buf); return driver ? gbinder_driver_io(driver) : NULL; } const GBinderRpcProtocol* gbinder_buffer_protocol( GBinderBuffer* buf) { GBinderDriver* driver = gbinder_buffer_driver(buf); return driver ? gbinder_driver_protocol(driver) : NULL; } void** gbinder_buffer_objects( GBinderBuffer* self) { if (G_LIKELY(self)) { GBinderBufferPriv* priv = gbinder_buffer_cast(self); if (priv->contents) { return priv->contents->objects; } } return NULL; } GBinderBufferContents* gbinder_buffer_contents( GBinderBuffer* self) { return G_LIKELY(self) ? gbinder_buffer_cast(self)->contents : NULL; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_buffer_p.h000066400000000000000000000064461526127616700201540ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_BUFFER_PRIVATE_H #define GBINDER_BUFFER_PRIVATE_H #include #include "gbinder_types_p.h" GBinderBuffer* gbinder_buffer_new( GBinderDriver* driver, void* data, gsize size, void** objects) GBINDER_INTERNAL; GBinderBuffer* gbinder_buffer_new_with_parent( GBinderBuffer* parent, void* data, gsize size) GBINDER_INTERNAL; GBinderDriver* gbinder_buffer_driver( GBinderBuffer* buf) GBINDER_INTERNAL; GBinderBufferContents* gbinder_buffer_contents( GBinderBuffer* buf) GBINDER_INTERNAL; gconstpointer gbinder_buffer_data( GBinderBuffer* buf, gsize* size) GBINDER_INTERNAL; const GBinderIo* gbinder_buffer_io( GBinderBuffer* buf) GBINDER_INTERNAL; const GBinderRpcProtocol* gbinder_buffer_protocol( GBinderBuffer* buf) GBINDER_INTERNAL; void** gbinder_buffer_objects( GBinderBuffer* buffer) GBINDER_INTERNAL; GBinderBufferContents* gbinder_buffer_contents_ref( GBinderBufferContents* contents) GBINDER_INTERNAL; void gbinder_buffer_contents_unref( GBinderBufferContents* contents) GBINDER_INTERNAL; GBinderBufferContentsList* gbinder_buffer_contents_list_add( GBinderBufferContentsList* list, GBinderBufferContents* contents) GBINDER_INTERNAL; GBinderBufferContentsList* gbinder_buffer_contents_list_dup( GBinderBufferContentsList* list) GBINDER_INTERNAL; void gbinder_buffer_contents_list_free( GBinderBufferContentsList* list) GBINDER_INTERNAL; #endif /* GBINDER_BUFFER_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_client.c000066400000000000000000000320511526127616700176240ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * 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_client_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_output_data.h" #include "gbinder_remote_object_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_local_request_p.h" #include "gbinder_log.h" #include #include #include #include typedef struct gbinder_client_iface_range { char* iface; GBytes* rpc_header; GBinderLocalRequest* basic_req; guint32 last_code; } GBinderClientIfaceRange; typedef struct gbinder_client_priv { GBinderClient pub; guint32 refcount; GBinderClientIfaceRange* ranges; guint nr; } GBinderClientPriv; typedef struct gbinder_client_tx { GBinderClient* client; GBinderClientReplyFunc reply; GDestroyNotify destroy; void* user_data; } GBinderClientTx; static inline GBinderClientPriv* gbinder_client_cast(GBinderClient* client) { return G_CAST(client, GBinderClientPriv, pub); } /*==========================================================================* * Implementation *==========================================================================*/ static const GBinderClientIfaceRange* gbinder_client_find_range( GBinderClientPriv* priv, guint32 code) { guint i; for (i = 0; i < priv->nr; i++) { const GBinderClientIfaceRange* r = priv->ranges + i; if (r->last_code >= code) { return r; } } return NULL; } /* * Generates basic request (without additional parameters) for the * specified interface and pulls header data out of it. The basic * request can be reused for those transactions which have no * additional parameters. The header data are needed for building * non-trivial requests. */ static void gbinder_client_init_range( GBinderClientIfaceRange* r, GBinderDriver* driver, const GBinderClientIfaceInfo* info) { GBinderOutputData* hdr; r->basic_req = gbinder_driver_local_request_new(driver, info->iface); hdr = gbinder_local_request_data(r->basic_req); r->rpc_header = g_bytes_new(hdr->bytes->data, hdr->bytes->len); r->iface = g_strdup(info->iface); r->last_code = info->last_code; } static int gbinder_client_sort_ranges( const void* p1, const void* p2) { const GBinderClientIfaceRange* r1 = p1; const GBinderClientIfaceRange* r2 = p2; return (r1->last_code < r2->last_code) ? (-1) : (r1->last_code > r2->last_code) ? 1 : 0; } static void gbinder_client_free( GBinderClientPriv* priv) { GBinderClient* self = &priv->pub; guint i; for (i = 0; i < priv->nr; i++) { GBinderClientIfaceRange* r = priv->ranges + i; gbinder_local_request_unref(r->basic_req); g_free(r->iface); if (r->rpc_header) { g_bytes_unref(r->rpc_header); } } g_free(priv->ranges); gbinder_remote_object_unref(self->remote); g_slice_free(GBinderClientPriv, priv); } static void gbinder_client_transact_reply( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* data) { GBinderClientTx* tx = data; if (tx->reply) { tx->reply(tx->client, reply, status, tx->user_data); } } static void gbinder_client_transact_destroy( gpointer data) { GBinderClientTx* tx = data; if (tx->destroy) { tx->destroy(tx->user_data); } gbinder_client_unref(tx->client); g_slice_free(GBinderClientTx, tx); } /*==========================================================================* * Internal interface *==========================================================================*/ GBinderRemoteReply* gbinder_client_transact_sync_reply2( GBinderClient* self, guint32 code, GBinderLocalRequest* req, int* status, const GBinderIpcSyncApi* api) { if (G_LIKELY(self)) { GBinderRemoteObject* obj = self->remote; if (G_LIKELY(!obj->dead)) { if (!req) { const GBinderClientIfaceRange* r = gbinder_client_find_range (gbinder_client_cast(self), code); /* Default empty request (just the header, no parameters) */ if (r) { req = r->basic_req; } } if (req) { return api->sync_reply(obj->ipc, obj->handle, code, req, status); } else { GWARN("Unable to build empty request for tx code %u", code); } } else { GDEBUG("Refusing to perform transaction with a dead object"); } } return NULL; } int gbinder_client_transact_sync_oneway2( GBinderClient* self, guint32 code, GBinderLocalRequest* req, const GBinderIpcSyncApi* api) { if (G_LIKELY(self)) { GBinderRemoteObject* obj = self->remote; if (G_LIKELY(!obj->dead)) { if (!req) { const GBinderClientIfaceRange* r = gbinder_client_find_range (gbinder_client_cast(self), code); /* Default empty request (just the header, no parameters) */ if (r) { req = r->basic_req; } } if (req) { return api->sync_oneway(obj->ipc, obj->handle, code, req); } else { GWARN("Unable to build empty request for tx code %u", code); } } else { GDEBUG("Refusing to perform transaction with a dead object"); return (-ESTALE); } } return (-EINVAL); } /*==========================================================================* * Interface *==========================================================================*/ GBinderClient* gbinder_client_new2( GBinderRemoteObject* remote, const GBinderClientIfaceInfo* ifaces, gsize count) { if (G_LIKELY(remote)) { GBinderClientPriv* priv = g_slice_new0(GBinderClientPriv); GBinderClient* self = &priv->pub; GBinderDriver* driver = remote->ipc->driver; g_atomic_int_set(&priv->refcount, 1); self->remote = gbinder_remote_object_ref(remote); if (count > 0) { gsize i; priv->nr = count; priv->ranges = g_new(GBinderClientIfaceRange, priv->nr); for (i = 0; i < count; i++) { gbinder_client_init_range(priv->ranges + i, driver, ifaces + i); } qsort(priv->ranges, count, sizeof(GBinderClientIfaceRange), gbinder_client_sort_ranges); } else { /* No interface info */ priv->nr = 1; priv->ranges = g_new0(GBinderClientIfaceRange, 1); priv->ranges[0].last_code = UINT_MAX; priv->ranges[0].basic_req = gbinder_local_request_new (gbinder_driver_io(driver), gbinder_driver_protocol(driver), NULL); } return self; } return NULL; } GBinderClient* gbinder_client_new( GBinderRemoteObject* remote, const char* iface) { GBinderClientIfaceInfo info; info.iface = iface; info.last_code = UINT_MAX; return gbinder_client_new2(remote, &info, 1); } GBinderClient* gbinder_client_ref( GBinderClient* self) { if (G_LIKELY(self)) { GBinderClientPriv* priv = gbinder_client_cast(self); GASSERT(priv->refcount > 0); g_atomic_int_inc(&priv->refcount); } return self; } void gbinder_client_unref( GBinderClient* self) { if (G_LIKELY(self)) { GBinderClientPriv* priv = gbinder_client_cast(self); GASSERT(priv->refcount > 0); if (g_atomic_int_dec_and_test(&priv->refcount)) { gbinder_client_free(priv); } } } const char* gbinder_client_interface( GBinderClient* self) /* since 1.0.22 */ { return G_LIKELY(self) ? gbinder_client_cast(self)->ranges->iface : NULL; } const char* gbinder_client_interface2( GBinderClient* self, guint32 code) /* since 1.0.42 */ { if (G_LIKELY(self)) { const GBinderClientIfaceRange* r = gbinder_client_find_range(gbinder_client_cast(self), code); if (r) { return r->iface; } } return NULL; } GBytes* gbinder_client_rpc_header( GBinderClient* self, guint32 code) /* since 1.1.14 */ { if (G_LIKELY(self)) { const GBinderClientIfaceRange* r = gbinder_client_find_range(gbinder_client_cast(self), code); if (r) { return r->rpc_header; } } return NULL; } GBinderLocalRequest* gbinder_client_new_request( GBinderClient* self) { if (G_LIKELY(self)) { GBinderClientPriv* priv = gbinder_client_cast(self); GBinderDriver* driver = self->remote->ipc->driver; return gbinder_local_request_new(gbinder_driver_io(driver), gbinder_driver_protocol(driver), priv->ranges->rpc_header); } return NULL; } GBinderLocalRequest* gbinder_client_new_request2( GBinderClient* self, guint32 code) /* since 1.0.42 */ { if (G_LIKELY(self)) { GBinderClientPriv* priv = gbinder_client_cast(self); const GBinderClientIfaceRange* range = gbinder_client_find_range (priv, code); if (range) { GBinderDriver* driver = self->remote->ipc->driver; return gbinder_local_request_new(gbinder_driver_io(driver), gbinder_driver_protocol(driver), range->rpc_header); } } return NULL; } GBinderRemoteReply* gbinder_client_transact_sync_reply( GBinderClient* self, guint32 code, GBinderLocalRequest* req, int* status) { return gbinder_client_transact_sync_reply2(self, code, req, status, &gbinder_ipc_sync_main); } int gbinder_client_transact_sync_oneway( GBinderClient* self, guint32 code, GBinderLocalRequest* req) { return gbinder_client_transact_sync_oneway2(self, code, req, &gbinder_ipc_sync_main); } gulong gbinder_client_transact( GBinderClient* self, guint32 code, guint32 flags, GBinderLocalRequest* req, GBinderClientReplyFunc reply, GDestroyNotify destroy, void* user_data) { if (G_LIKELY(self)) { GBinderRemoteObject* obj = self->remote; if (G_LIKELY(!obj->dead)) { if (!req) { const GBinderClientIfaceRange* r = gbinder_client_find_range (gbinder_client_cast(self), code); /* Default empty request (just the header, no parameters) */ if (r) { req = r->basic_req; } } if (req) { GBinderClientTx* tx = g_slice_new0(GBinderClientTx); tx->client = gbinder_client_ref(self); tx->reply = reply; tx->destroy = destroy; tx->user_data = user_data; return gbinder_ipc_transact(obj->ipc, obj->handle, code, flags, req, gbinder_client_transact_reply, gbinder_client_transact_destroy, tx); } else { GWARN("Unable to build empty request for tx code %u", code); } } else { GDEBUG("Refusing to perform transaction with a dead object"); } } return 0; } void gbinder_client_cancel( GBinderClient* self, gulong id) { if (G_LIKELY(self)) { gbinder_ipc_cancel(gbinder_client_ipc(self), id); } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_client_p.h000066400000000000000000000047031526127616700201530ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_CLIENT_PRIVATE_H #define GBINDER_CLIENT_PRIVATE_H #include #include "gbinder_types_p.h" struct gbinder_client { GBinderRemoteObject* remote; }; GBinderRemoteReply* gbinder_client_transact_sync_reply2( GBinderClient* self, guint32 code, GBinderLocalRequest* req, int* status, const GBinderIpcSyncApi* api) G_GNUC_WARN_UNUSED_RESULT GBINDER_INTERNAL; int gbinder_client_transact_sync_oneway2( GBinderClient* self, guint32 code, GBinderLocalRequest* req, const GBinderIpcSyncApi* api) GBINDER_INTERNAL; #define gbinder_client_ipc(client) ((client)->remote->ipc) #endif /* GBINDER_CLIENT_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_config.c000066400000000000000000000323371526127616700176220ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_config.h" #include "gbinder_eventloop_p.h" #include "gbinder_log.h" #include #include #include #include /* * The contents of the config file is queried from (at least) two places, * and pretty much always this happens the same stack. Which means that * we can avoid reading the same file twice if we delay dereferencing of * GKeyFile until the next idle loop. */ static GKeyFile* gbinder_config_keyfile = NULL; static GBinderEventLoopCallback* gbinder_config_autorelease = NULL; static const char gbinder_config_suffix[] = ".conf"; static const char gbinder_config_default_file[] = "/etc/gbinder.conf"; static const char gbinder_config_default_dir[] = "/etc/gbinder.d"; const char* gbinder_config_file = gbinder_config_default_file; const char* gbinder_config_dir = gbinder_config_default_dir; /* * Presets for particular API level can be chosen with ApiLevel * setting, e.g. * * [General] * ApiLevel=29 * */ static const char CONF_GENERAL[] = "General"; static const char CONG_API_LEVEL[] = "ApiLevel"; typedef struct gbinder_config_preset_entry { const char* key; const char* value; } GBinderConfigPresetEntry; typedef struct gbinder_config_preset_group { const char* name; const GBinderConfigPresetEntry* entries; } GBinderConfigPresetGroup; typedef struct gbinder_config_preset { guint api_level; const GBinderConfigPresetGroup* groups; } GBinderConfigPreset; /* API level 28 */ static const GBinderConfigPresetEntry gbinder_config_28_servicemanager[] = { { "/dev/binder", "aidl2" }, { "/dev/vndbinder", "aidl2" }, { NULL, NULL } }; static const GBinderConfigPresetGroup gbinder_config_28[] = { { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_28_servicemanager }, { NULL, NULL } }; /* API level 29 */ static const GBinderConfigPresetEntry gbinder_config_29_protocol[] = { { "/dev/binder", "aidl2" }, { "/dev/vndbinder", "aidl2" }, { NULL, NULL } }; #define gbinder_config_29_servicemanager gbinder_config_28_servicemanager static const GBinderConfigPresetGroup gbinder_config_29[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_29_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_29_servicemanager }, { NULL, NULL } }; /* API level 30 */ static const GBinderConfigPresetEntry gbinder_config_30_protocol[] = { { "/dev/binder", "aidl3" }, { "/dev/vndbinder", "aidl3" }, { NULL, NULL } }; static const GBinderConfigPresetEntry gbinder_config_30_servicemanager[] = { { "/dev/binder", "aidl3" }, { "/dev/vndbinder", "aidl3" }, { NULL, NULL } }; static const GBinderConfigPresetGroup gbinder_config_30[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_30_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_30_servicemanager }, { NULL, NULL } }; /* API level 31 */ static const GBinderConfigPresetEntry gbinder_config_31_protocol[] = { { "/dev/binder", "aidl4" }, { "/dev/vndbinder", "aidl4" }, { NULL, NULL } }; static const GBinderConfigPresetGroup gbinder_config_31[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_31_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_30_servicemanager }, { NULL, NULL } }; /* API level 33 - reverts back to AIDL3 protocol */ static const GBinderConfigPresetGroup gbinder_config_33[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_30_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_30_servicemanager }, { NULL, NULL } }; /* API level 35 */ static const GBinderConfigPresetEntry gbinder_config_35_servicemanager[] = { { "/dev/binder", "aidl5" }, { "/dev/vndbinder", "aidl5" }, { NULL, NULL } }; static const GBinderConfigPresetGroup gbinder_config_35[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_30_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_35_servicemanager }, { NULL, NULL } }; /* API level 36 */ static const GBinderConfigPresetEntry gbinder_config_36_servicemanager[] = { { "/dev/binder", "aidl6" }, { "/dev/vndbinder", "aidl6" }, { NULL, NULL } }; static const GBinderConfigPresetGroup gbinder_config_36[] = { { GBINDER_CONFIG_GROUP_PROTOCOL, gbinder_config_30_protocol }, { GBINDER_CONFIG_GROUP_SERVICEMANAGER, gbinder_config_36_servicemanager }, { NULL, NULL } }; /* Presets sorted by API level in descending order */ static const GBinderConfigPreset gbinder_config_presets[] = { { 36, gbinder_config_36 }, { 35, gbinder_config_35 }, { 33, gbinder_config_33 }, { 31, gbinder_config_31 }, { 30, gbinder_config_30 }, { 29, gbinder_config_29 }, { 28, gbinder_config_28 } }; static char** gbinder_config_collect_files( const char* path, const char* suffix) { /* * Returns sorted list of regular files in the directory, * optionally having the specified suffix (e.g. ".conf"). * Returns NULL if nothing appropriate has been found. */ char** files = NULL; if (path) { GDir* dir = g_dir_open(path, 0, NULL); if (dir) { GPtrArray* list = g_ptr_array_new(); const gchar* name; while ((name = g_dir_read_name(dir)) != NULL) { if (g_str_has_suffix(name, suffix)) { char* fullname = g_build_filename(path, name, NULL); struct stat st; if (!stat(fullname, &st) && S_ISREG(st.st_mode)) { g_ptr_array_add(list, fullname); } else { g_free(fullname); } } } if (list->len > 0) { g_ptr_array_add(list, NULL); files = (char**) g_ptr_array_free(list, FALSE); gutil_strv_sort(files, TRUE); } else { g_ptr_array_free(list, TRUE); } g_dir_close(dir); } } return files; } static GKeyFile* gbinder_config_merge_keyfiles( GKeyFile* dest, GKeyFile* src) { gsize i, ngroups; gchar** groups = g_key_file_get_groups(src, &ngroups); for (i = 0; i < ngroups; i++) { gsize k, nkeys; const char* group = groups[i]; char** keys = g_key_file_get_keys(src, group, &nkeys, NULL); for (k = 0; k < nkeys; k++) { const char* key = keys[k]; char* value = g_key_file_get_value(src, group, key, NULL); g_key_file_set_value(dest, group, key, value); g_free(value); } g_strfreev(keys); } g_strfreev(groups); return dest; } static void gbinder_config_apply_presets( GKeyFile* config, const GBinderConfigPreset* preset) { const GBinderConfigPresetGroup* g; GDEBUG("Applying presets for API level %d", preset->api_level); for (g = preset->groups; g->name; g++) { const GBinderConfigPresetEntry* e; for (e = g->entries; e->key; e++) { if (!g_key_file_has_key(config, g->name, e->key, NULL)) { g_key_file_set_value(config, g->name, e->key, e->value); } } } } static GKeyFile* gbinder_config_load_files() { GError* error = NULL; GKeyFile* out = NULL; char** files = gbinder_config_collect_files(gbinder_config_dir, gbinder_config_suffix); if (gbinder_config_file && g_file_test(gbinder_config_file, G_FILE_TEST_EXISTS)) { out = g_key_file_new(); if (g_key_file_load_from_file(out, gbinder_config_file, G_KEY_FILE_NONE, &error)) { GDEBUG("Loaded %s", gbinder_config_file); } else { GERR("Error loading %s: %s", gbinder_config_file, error->message); g_error_free(error); error = NULL; gbinder_config_file = NULL; /* Don't retry */ g_key_file_unref(out); out = NULL; } } /* Files in the config directory overwrite /etc/gbinder.conf */ if (files) { char** ptr; GKeyFile* override = NULL; for (ptr = files; *ptr; ptr++) { const char* file = *ptr; if (!override) { override = g_key_file_new(); } if (g_key_file_load_from_file(override, file, G_KEY_FILE_NONE, &error)) { GDEBUG("Loaded %s", file); if (!out) { out = override; override = NULL; } else { out = gbinder_config_merge_keyfiles(out, override); } } else { GERR("Error loading %s: %s", file, error->message); g_error_free(error); error = NULL; } } g_strfreev(files); if (override) { g_key_file_unref(override); } } if (out) { /* Apply presets */ const int api_level = g_key_file_get_integer(out, CONF_GENERAL, CONG_API_LEVEL, NULL); if (api_level > 0) { int i; GDEBUG("API level %d", api_level); for (i = 0; i < G_N_ELEMENTS(gbinder_config_presets); i++) { const GBinderConfigPreset* preset = gbinder_config_presets + i; if (api_level >= preset->api_level) { gbinder_config_apply_presets(out, preset); break; } } } } return out; } static void gbinder_config_autorelease_cb( gpointer data) { GASSERT(gbinder_config_keyfile == data); gbinder_config_keyfile = NULL; g_key_file_unref(data); } GKeyFile* /* autoreleased */ gbinder_config_get() { if (!gbinder_config_keyfile && (gbinder_config_file || gbinder_config_dir)) { gbinder_config_keyfile = gbinder_config_load_files(); if (gbinder_config_keyfile) { /* See the comment at the top of the file why this is needed */ gbinder_config_autorelease = gbinder_idle_callback_schedule_new (gbinder_config_autorelease_cb, gbinder_config_keyfile, NULL); } } return gbinder_config_keyfile; } /* Helper for loading config group in device = ident format */ GHashTable* gbinder_config_load( const char* group, GBinderConfigValueMapFunc mapper) { GKeyFile* k = gbinder_config_get(); GHashTable* map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (k) { gsize n; char** devs = g_key_file_get_keys(k, group, &n, NULL); if (devs) { gsize i; for (i = 0; i < n; i++) { char* dev = devs[i]; char* sval = g_key_file_get_value(k, group, dev, NULL); gconstpointer val = mapper(sval); if (val) { g_hash_table_replace(map, dev, (gpointer) val); } else { GWARN("Unknown gbinder config '%s' for %s in group [%s]", sval, dev, group); g_free(dev); } g_free(sval); } /* Shallow delete (contents got stolen or freed) */ g_free(devs); } } return map; } void gbinder_config_exit() { if (gbinder_config_autorelease) { gbinder_idle_callback_destroy(gbinder_config_autorelease); gbinder_config_autorelease = NULL; } if (gbinder_config_keyfile) { g_key_file_unref(gbinder_config_keyfile); gbinder_config_keyfile = NULL; } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_config.h000066400000000000000000000051311526127616700176170ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_CONFIG_H #define GBINDER_CONFIG_H #include "gbinder_types_p.h" typedef gconstpointer (*GBinderConfigValueMapFunc)( const char* value); GHashTable* gbinder_config_load( const char* group, GBinderConfigValueMapFunc map) GBINDER_INTERNAL; GKeyFile* /* autoreleased */ gbinder_config_get( void) GBINDER_INTERNAL; /* This one declared strictly for unit tests */ void gbinder_config_exit( void) GBINDER_INTERNAL GBINDER_DESTRUCTOR; /* And these too */ extern const char* gbinder_config_file GBINDER_INTERNAL; extern const char* gbinder_config_dir GBINDER_INTERNAL; /* Configuration groups and special value */ #define GBINDER_CONFIG_GROUP_PROTOCOL "Protocol" #define GBINDER_CONFIG_GROUP_SERVICEMANAGER "ServiceManager" #define GBINDER_CONFIG_VALUE_DEFAULT "Default" #endif /* GBINDER_CONFIG_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_driver.c000066400000000000000000001166201526127616700176460ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * 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_driver.h" #include "gbinder_buffer_p.h" #include "gbinder_handler.h" #include "gbinder_io.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_local_request_p.h" #include "gbinder_object_registry.h" #include "gbinder_output_data.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_reply_p.h" #include "gbinder_remote_request_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_system.h" #include "gbinder_writer.h" #include "gbinder_log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* BINDER_VM_SIZE copied from native/libs/binder/ProcessState.cpp */ #define BINDER_VM_SIZE ((1024*1024) - sysconf(_SC_PAGE_SIZE)*2) #define BINDER_MAX_REPLY_SIZE (256) /* ioctl code (the only one we really need here) */ #define BINDER_VERSION _IOWR('b', 9, gint32) /* OK, one more */ #define BINDER_SET_MAX_THREADS _IOW('b', 5, guint32) #define DEFAULT_MAX_BINDER_THREADS (0) struct gbinder_driver { gint refcount; int fd; void* vm; gsize vmsize; char* dev; const char* name; const GBinderIo* io; const GBinderRpcProtocol* protocol; }; typedef struct gbinder_driver_read_buf { GBinderIoBuf io; gsize offset; } GBinderDriverReadBuf; typedef struct gbinder_driver_read_data { GBinderDriverReadBuf buf; guint8 data[GBINDER_IO_READ_BUFFER_SIZE]; } GBinderDriverReadData; typedef struct gbinder_driver_context { GBinderDriverReadBuf* rbuf; GBinderObjectRegistry* reg; GBinderHandler* handler; GBinderBufferContentsList* bufs; GUtilCleanup* cleanup; } GBinderDriverContext; static int gbinder_driver_txstatus( GBinderDriver* self, GBinderDriverContext* context, GBinderRemoteReply* reply); /*==========================================================================* * Implementation *==========================================================================*/ #if GUTIL_LOG_VERBOSE static void gbinder_driver_verbose_dump( const char mark, uintptr_t ptr, gsize len) { /* The caller should make sure that verbose log is enabled */ if (len > 0 && GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) { char prefix[3]; char line[GUTIL_HEXDUMP_BUFSIZE]; prefix[0] = mark; prefix[1] = ' '; prefix[2] = 0; while (len > 0) { const guint dumped = gutil_hexdump(line, (void*)ptr, len); GVERBOSE("%s%s", prefix, line); len -= dumped; ptr += dumped; prefix[0] = ' '; } } } GBINDER_INLINE_FUNC void gbinder_driver_verbose_dump_bytes( const char mark, const GByteArray* bytes) { gbinder_driver_verbose_dump(mark, (uintptr_t)bytes->data, bytes->len); } static void gbinder_driver_verbose_transaction_data( const char* name, const GBinderIoTxData* tx) { if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) { if (tx->objects) { guint n = 0; while (tx->objects[n]) n++; if (tx->status) { if (tx->target) { GVERBOSE("> %s %p %d (%u bytes, %u objects)", name, tx->target, tx->status, (guint)tx->size, n); } else { GVERBOSE("> %s %d (%u bytes, %u objects)", name, tx->status, (guint)tx->size, n); } } else if (tx->code) { if (tx->target) { GVERBOSE("> %s %p 0x%08x (%u bytes, %u objects)", name, tx->target, tx->code, (guint)tx->size, n); } else { GVERBOSE("> %s 0x%08x (%u bytes, %u objects)", name, tx->code, (guint)tx->size, n); } } else { if (tx->target) { GVERBOSE("> %s %p (%u bytes, %u objects)", name, tx->target, (guint)tx->size, n); } else { GVERBOSE("> %s (%u bytes, %u objects)", name, (guint) tx->size, n); } } } else { if (tx->status) { if (tx->target) { GVERBOSE("> %s %p %d (%u bytes)", name, tx->target, tx->status, (guint)tx->size); } else { GVERBOSE("> %s %d (%u bytes)", name, tx->status, (guint) tx->size); } } else if (tx->code) { if (tx->target) { GVERBOSE("> %s %p 0x%08x (%u bytes)", name, tx->target, tx->code, (guint)tx->size); } else { GVERBOSE("> %s 0x%08x (%u bytes)", name, tx->code, (guint)tx->size); } } else { if (tx->target) { GVERBOSE("> %s %p (%u bytes)", name, tx->target, (guint) tx->size); } else { GVERBOSE("> %s (%u bytes)", name, (guint)tx->size); } } } } } #else # define gbinder_driver_verbose_dump(x,y,z) GLOG_NOTHING # define gbinder_driver_verbose_dump_bytes(x,y) GLOG_NOTHING # define gbinder_driver_verbose_transaction_data(x,y) GLOG_NOTHING #endif /* GUTIL_LOG_VERBOSE */ static int gbinder_driver_write( GBinderDriver* self, GBinderIoBuf* buf) { int err = (-EAGAIN); while (err == (-EAGAIN)) { gbinder_driver_verbose_dump('<', buf->ptr + buf->consumed, buf->size - buf->consumed); GVERBOSE("gbinder_driver_write(%d) %u/%u", self->fd, (guint)buf->consumed, (guint)buf->size); err = self->io->write_read(self->fd, buf, NULL); GVERBOSE("gbinder_driver_write(%d) %u/%u err %d", self->fd, (guint)buf->consumed, (guint)buf->size, err); } return err; } static int gbinder_driver_write_read( GBinderDriver* self, GBinderIoBuf* write, GBinderDriverReadBuf* rbuf) { int err = (-EAGAIN); GBinderIoBuf rio; GBinderIoBuf* read; /* rbuf is never NULL */ if (rbuf->offset) { rio.ptr = rbuf->io.ptr + rbuf->offset; rio.size = rbuf->io.size - rbuf->offset; rio.consumed = rbuf->io.consumed - rbuf->offset; read = &rio; } else { read = &rbuf->io; } while (err == (-EAGAIN)) { #if GUTIL_LOG_VERBOSE const gsize were_consumed = read ? read->consumed : 0; if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) { if (write) { gbinder_driver_verbose_dump('<', write->ptr + write->consumed, write->size - write->consumed); } GVERBOSE("gbinder_driver_write_read(%d) " "write %u/%u read %u/%u", self->fd, (guint)(write ? write->consumed : 0), (guint)(write ? write->size : 0), (guint)(read ? read->consumed : 0), (guint)(read ? read->size : 0)); } #endif /* GUTIL_LOG_VERBOSE */ err = self->io->write_read(self->fd, write, read); #if GUTIL_LOG_VERBOSE if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) { GVERBOSE("gbinder_driver_write_read(%d) " "write %u/%u read %u/%u err %d", self->fd, (guint)(write ? write->consumed : 0), (guint)(write ? write->size : 0), (guint)(read ? read->consumed : 0), (guint)(read ? read->size : 0), err); if (read) { gbinder_driver_verbose_dump('>', read->ptr + were_consumed, read->consumed - were_consumed); } } #endif /* GUTIL_LOG_VERBOSE */ } if (rbuf->offset) { rbuf->io.consumed = rio.consumed + rbuf->offset; } return err; } static gboolean gbinder_driver_cmd( GBinderDriver* self, guint32 cmd) { GBinderIoBuf write; memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)&cmd; write.size = sizeof(cmd); return gbinder_driver_write(self, &write) >= 0; } static gboolean gbinder_driver_cmd_int32( GBinderDriver* self, guint32 cmd, guint32 param) { GBinderIoBuf write; guint32 data[2]; data[0] = cmd; data[1] = param; memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)data; write.size = sizeof(data); return gbinder_driver_write(self, &write) >= 0; } static gboolean gbinder_driver_cmd_data( GBinderDriver* self, guint32 cmd, const void* payload, void* buf) { GBinderIoBuf write; guint32* data = buf; data[0] = cmd; memcpy(data + 1, payload, _IOC_SIZE(cmd)); memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)buf; write.size = 4 + _IOC_SIZE(cmd); return gbinder_driver_write(self, &write) >= 0; } static gboolean gbinder_driver_handle_cookie( GBinderDriver* self, guint32 cmd, GBinderRemoteObject* obj) { GBinderIoBuf write; guint8 buf[4 + GBINDER_MAX_HANDLE_COOKIE_SIZE]; guint32* data = (guint32*)buf; data[0] = cmd; memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)buf; write.size = 4 + self->io->encode_handle_cookie(data + 1, obj); return gbinder_driver_write(self, &write) >= 0; } static void gbinder_driver_read_init( GBinderDriverReadData* read) { /* * It shouldn't be necessary to zero-initialize the whole buffer * but valgrind complains about access to uninitialised data if * we don't do so. Oh well... */ memset(read, 0, sizeof(*read)); read->buf.io.ptr = GPOINTER_TO_SIZE(read->data); read->buf.io.size = sizeof(read->data); } static void gbinder_driver_context_init( GBinderDriverContext* context, GBinderDriverReadBuf* rbuf, GBinderObjectRegistry* reg, GBinderHandler* handler) { context->rbuf = rbuf; context->reg = reg; context->handler = handler; context->cleanup = NULL; context->bufs = NULL; } static void gbinder_driver_context_cleanup_add( GBinderDriverContext* context, GDestroyNotify destroy, gpointer pointer) { if (!context->cleanup) { context->cleanup = gutil_cleanup_new(); } gutil_cleanup_add(context->cleanup, destroy, pointer); } static void gbinder_driver_context_cleanup( GBinderDriverContext* context) { gutil_cleanup_free(context->cleanup); gbinder_buffer_contents_list_free(context->bufs); } static guint32 gbinder_driver_next_command( GBinderDriver* self, const GBinderDriverReadBuf* rbuf) { if (rbuf->io.consumed > rbuf->offset) { const gsize remaining = rbuf->io.consumed - rbuf->offset; if (remaining >= 4) { /* The size of the data to follow is encoded in the command code */ const guint32 cmd = *(guint32*)(rbuf->io.ptr + rbuf->offset); const guint datalen = _IOC_SIZE(cmd); if (remaining >= 4 + datalen) { return cmd; } } } return 0; } static gboolean gbinder_driver_reply_status( GBinderDriver* self, gint32 status) { const GBinderIo* io = self->io; GBinderIoBuf write; guint8 buf[sizeof(guint32) + GBINDER_MAX_BC_TRANSACTION_SIZE]; guint8* ptr = buf; const guint32* code = &io->bc.reply; /* Command (this has to be slightly convoluted to avoid breaking * strict-aliasing rules.. oh well) */ memcpy(buf, code, sizeof(*code)); ptr += sizeof(*code); /* Data */ ptr += io->encode_status_reply(ptr, &status); GVERBOSE("< BC_REPLY (%d)", status); memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)buf; write.size = ptr - buf; return gbinder_driver_write(self, &write) >= 0; } static gboolean gbinder_driver_reply_data( GBinderDriver* self, GBinderOutputData* data) { GBinderIoBuf write; const GBinderIo* io = self->io; const gsize extra_buffers = gbinder_output_data_buffers_size(data); guint8 buf[GBINDER_MAX_BC_TRANSACTION_SG_SIZE + sizeof(guint32)]; guint32* cmd = (guint32*)buf; guint len = sizeof(*cmd); int status; GUtilIntArray* offsets = gbinder_output_data_offsets(data); void* offsets_buf = NULL; /* Build BC_REPLY */ if (extra_buffers) { GVERBOSE("< BC_REPLY_SG %u bytes", (guint)extra_buffers); gbinder_driver_verbose_dump_bytes(' ', data->bytes); *cmd = io->bc.reply_sg; len += io->encode_reply_sg(buf + len, 0, 0, data->bytes, offsets, &offsets_buf, extra_buffers); } else { GVERBOSE("< BC_REPLY"); gbinder_driver_verbose_dump_bytes(' ', data->bytes); *cmd = io->bc.reply; len += io->encode_reply(buf + len, 0, 0, data->bytes, offsets, &offsets_buf); } #if 0 /* GUTIL_LOG_VERBOSE */ if (offsets && offsets->count) { gbinder_driver_verbose_dump('<', (uintptr_t)offsets_buf, offsets->count * io->pointer_size); } #endif /* GUTIL_LOG_VERBOSE */ /* Write it */ write.ptr = (uintptr_t)buf; write.size = len; write.consumed = 0; status = gbinder_driver_write(self, &write); g_free(offsets_buf); return status >= 0; } static void gbinder_driver_handle_transaction( GBinderDriver* self, GBinderDriverContext* context, const void* data) { GBinderLocalReply* reply = NULL; GBinderObjectRegistry* reg = context->reg; GBinderRemoteRequest* req; GBinderIoTxData tx; GBinderLocalObject* obj; const char* iface; int txstatus = -EBADMSG; self->io->decode_transaction_data(data, &tx); gbinder_driver_verbose_transaction_data("BR_TRANSACTION", &tx); req = gbinder_remote_request_new(reg, self->protocol, tx.pid, tx.euid); obj = gbinder_object_registry_get_local(reg, tx.target); /* Transfer data ownership to the request */ if (tx.data && tx.size) { GBinderBuffer* buf = gbinder_buffer_new(self, tx.data, tx.size, tx.objects); gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size); gbinder_remote_request_set_data(req, tx.code, buf); context->bufs = gbinder_buffer_contents_list_add(context->bufs, gbinder_buffer_contents(buf)); } else { GASSERT(!tx.objects); gbinder_driver_free_buffer(self, tx.data); } /* Process the transaction (NULL is properly handled) */ iface = gbinder_remote_request_interface(req); switch (gbinder_local_object_can_handle_transaction(obj, iface, tx.code)) { case GBINDER_LOCAL_TRANSACTION_LOOPER: reply = gbinder_local_object_handle_looper_transaction(obj, req, tx.code, tx.flags, &txstatus); break; case GBINDER_LOCAL_TRANSACTION_SUPPORTED: /* * NULL GBinderHandler means that this is a synchronous call * executed on the main thread, meaning that we can call the * local object directly. */ reply = context->handler ? gbinder_handler_transact(context->handler, obj, req, tx.code, tx.flags, &txstatus) : gbinder_local_object_handle_transaction(obj, req, tx.code, tx.flags, &txstatus); break; default: GWARN("Unhandled transaction %s 0x%08x from %s", iface, tx.code, self->name); break; } /* No reply for one-way transactions */ if (!(tx.flags & GBINDER_TX_FLAG_ONEWAY)) { if (reply) { context->bufs = gbinder_buffer_contents_list_add(context->bufs, gbinder_local_reply_contents(reply)); gbinder_driver_reply_data(self, gbinder_local_reply_data(reply)); } else { gbinder_driver_reply_status(self, txstatus); } /* Wait until the reply is handled */ do { txstatus = gbinder_driver_write_read(self, NULL, context->rbuf); if (txstatus >= 0) { txstatus = gbinder_driver_txstatus(self, context, NULL); } } while (txstatus == (-EAGAIN)); } /* Free the data allocated for the transaction */ gbinder_remote_request_unref(req); gbinder_local_reply_unref(reply); gbinder_local_object_unref(obj); } static void gbinder_driver_cleanup_decrefs( gpointer pointer) { GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(pointer); gbinder_local_object_handle_decrefs(obj); gbinder_local_object_unref(obj); } static void gbinder_driver_cleanup_release( gpointer pointer) { GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(pointer); gbinder_local_object_handle_release(obj); gbinder_local_object_unref(obj); } static void gbinder_driver_handle_command( GBinderDriver* self, GBinderDriverContext* context, guint32 cmd, const void* data) { const GBinderIo* io = self->io; GBinderObjectRegistry* reg = context->reg; if (cmd == io->br.noop) { GVERBOSE("> BR_NOOP"); } else if (cmd == io->br.ok) { GVERBOSE("> BR_OK"); } else if (cmd == io->br.transaction_complete) { GVERBOSE("> BR_TRANSACTION_COMPLETE (?)"); } else if (cmd == io->br.spawn_looper) { GVERBOSE("> BR_SPAWN_LOOPER"); } else if (cmd == io->br.finished) { GVERBOSE("> BR_FINISHED"); } else if (cmd == io->br.increfs) { guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE]; GBinderLocalObject* obj = gbinder_object_registry_get_local (reg, io->decode_ptr_cookie(data)); GVERBOSE("> BR_INCREFS %p", obj); gbinder_local_object_handle_increfs(obj); gbinder_local_object_unref(obj); GVERBOSE("< BC_INCREFS_DONE %p", obj); gbinder_driver_cmd_data(self, io->bc.increfs_done, data, buf); } else if (cmd == io->br.decrefs) { GBinderLocalObject* obj = gbinder_object_registry_get_local (reg, io->decode_ptr_cookie(data)); GVERBOSE("> BR_DECREFS %p", obj); if (obj) { /* * Unrefs must be processed only after clearing the incoming * command queue. */ gbinder_driver_context_cleanup_add(context, gbinder_driver_cleanup_decrefs, obj); } } else if (cmd == io->br.acquire) { guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE]; GBinderLocalObject* obj = gbinder_object_registry_get_local (reg, io->decode_ptr_cookie(data)); GVERBOSE("> BR_ACQUIRE %p", obj); if (obj) { /* BC_ACQUIRE_DONE will be sent after the request is handled */ gbinder_local_object_handle_acquire(obj, context->bufs); gbinder_local_object_unref(obj); } else { /* This shouldn't normally happen. Just send the same data back. */ GVERBOSE("< BC_ACQUIRE_DONE"); gbinder_driver_cmd_data(self, io->bc.acquire_done, data, buf); } } else if (cmd == io->br.release) { GBinderLocalObject* obj = gbinder_object_registry_get_local (reg, io->decode_ptr_cookie(data)); GVERBOSE("> BR_RELEASE %p", obj); if (obj) { /* * Unrefs must be processed only after clearing the incoming * command queue. */ gbinder_driver_context_cleanup_add(context, gbinder_driver_cleanup_release, obj); } } else if (cmd == io->br.transaction) { gbinder_driver_handle_transaction(self, context, data); } else if (cmd == io->br.dead_binder) { guint64 handle = 0; GBinderRemoteObject* obj; io->decode_cookie(data, &handle); GVERBOSE("> BR_DEAD_BINDER 0x%08llx", (long long unsigned int) handle); obj = gbinder_object_registry_get_remote(reg, (guint32)handle, REMOTE_REGISTRY_DONT_CREATE); if (obj) { /* BC_DEAD_BINDER_DONE will be sent after the request is handled */ gbinder_remote_object_handle_death_notification(obj); gbinder_remote_object_unref(obj); } else { guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE]; /* This shouldn't normally happen. Just send the same data back. */ GVERBOSE("< BC_DEAD_BINDER_DONE 0x%08llx", (long long unsigned int) handle); gbinder_driver_cmd_data(self, io->bc.dead_binder_done, data, buf); } } else if (cmd == io->br.clear_death_notification_done) { #if GUTIL_LOG_VERBOSE if (GLOG_ENABLED(GLOG_LEVEL_VERBOSE)) { guint64 handle = 0; io->decode_cookie(data, &handle); GVERBOSE("> BR_CLEAR_DEATH_NOTIFICATION_DONE 0x%08llx", (long long unsigned int) handle); } #endif /* GUTIL_LOG_VERBOSE */ } else { #pragma message("TODO: handle more commands from the driver") GWARN("Unexpected command 0x%08x", cmd); } } static void gbinder_driver_compact_read_buf( GBinderDriverReadBuf* buf) { /* * Move the data to the beginning of the buffer to make room for the * next portion of data (in case if we need one) */ if (buf->io.consumed > buf->offset) { const gsize unprocessed = buf->io.consumed - buf->offset; guint8* data = GSIZE_TO_POINTER(buf->io.ptr); memmove(data, data + buf->offset, unprocessed); buf->io.consumed = unprocessed; } else { buf->io.consumed = 0; } buf->offset = 0; } static void gbinder_driver_handle_commands( GBinderDriver* self, GBinderDriverContext* context) { GBinderDriverReadBuf* rbuf = context->rbuf; guint32 cmd; while ((cmd = gbinder_driver_next_command(self, rbuf)) != 0) { const gsize datalen = _IOC_SIZE(cmd); const gsize total = datalen + sizeof(cmd); const void* data = GSIZE_TO_POINTER(rbuf->io.ptr + rbuf->offset + 4); /* Handle this command */ rbuf->offset += total; gbinder_driver_handle_command(self, context, cmd, data); } gbinder_driver_compact_read_buf(rbuf); } static int gbinder_driver_txstatus( GBinderDriver* self, GBinderDriverContext* context, GBinderRemoteReply* reply) { guint32 cmd; int txstatus = (-EAGAIN); GBinderDriverReadBuf* rbuf = context->rbuf; const guint8* buf = GSIZE_TO_POINTER(rbuf->io.ptr); const GBinderIo* io = self->io; while (txstatus == (-EAGAIN) && (cmd = gbinder_driver_next_command(self, context->rbuf)) != 0) { /* The size of the data is encoded in the command code */ const gsize datalen = _IOC_SIZE(cmd); const gsize total = datalen + sizeof(cmd); const void* data = buf + rbuf->offset + sizeof(cmd); /* Swallow this packet */ rbuf->offset += total; /* Handle the command */ if (cmd == io->br.transaction_complete) { GVERBOSE("> BR_TRANSACTION_COMPLETE"); if (!reply) { txstatus = GBINDER_STATUS_OK; } } else if (cmd == io->br.dead_reply) { GVERBOSE("> BR_DEAD_REPLY"); txstatus = GBINDER_STATUS_DEAD_OBJECT; } else if (cmd == io->br.failed_reply) { GVERBOSE("> BR_FAILED_REPLY"); txstatus = GBINDER_STATUS_FAILED; } else if (cmd == io->br.reply) { GBinderIoTxData tx; io->decode_transaction_data(data, &tx); gbinder_driver_verbose_transaction_data("BR_REPLY", &tx); /* Transfer data ownership to the reply */ if (tx.data && tx.size && reply) { GBinderBuffer* buf = gbinder_buffer_new(self, tx.data, tx.size, tx.objects); gbinder_driver_verbose_dump(' ', (uintptr_t)tx.data, tx.size); gbinder_remote_reply_set_data(reply, buf); context->bufs = gbinder_buffer_contents_list_add(context->bufs, gbinder_buffer_contents(buf)); } else { GASSERT(!tx.objects); gbinder_driver_free_buffer(self, tx.data); } /* * Filter out special cases. It's a bit unfortunate that * libgbinder API historically mixed TF_STATUS_CODE payload * with special delivery errors. It's not a bit deal though, * because in real life TF_STATUS_CODE transactions are not * being used that often, if at all. */ switch (tx.status) { case (-EAGAIN): case GBINDER_STATUS_FAILED: case GBINDER_STATUS_DEAD_OBJECT: txstatus = (-EFAULT); GWARN("Replacing tx status %d with %d", tx.status, txstatus); break; default: txstatus = tx.status; break; } } else { gbinder_driver_handle_command(self, context, cmd, data); } } gbinder_driver_compact_read_buf(rbuf); return txstatus; } /*==========================================================================* * Interface * * This is an internal module, we can assume that GBinderDriver pointer * is never NULL, GBinderIpc makes sure of that. *==========================================================================*/ GBinderDriver* gbinder_driver_new( const char* dev, const GBinderRpcProtocol* protocol) { const int fd = gbinder_system_open(dev, O_RDWR | O_CLOEXEC); if (fd >= 0) { gint32 version = 0; if (gbinder_system_ioctl(fd, BINDER_VERSION, &version) >= 0) { const GBinderIo* io = NULL; /* Decide which kernel we are dealing with */ GDEBUG("Opened %s version %d", dev, version); if (version == gbinder_io_32.version) { io = &gbinder_io_32; } else if (version == gbinder_io_64.version) { io = &gbinder_io_64; } else { GERR("%s unexpected version %d", dev, version); } if (io) { /* mmap the binder, providing a chunk of virtual address * space to receive transactions. */ const gsize vmsize = BINDER_VM_SIZE; void* vm = gbinder_system_mmap(vmsize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd); if (vm != MAP_FAILED) { guint32 max_threads = DEFAULT_MAX_BINDER_THREADS; GBinderDriver* self = g_slice_new0(GBinderDriver); g_atomic_int_set(&self->refcount, 1); self->fd = fd; self->io = io; self->vm = vm; self->vmsize = vmsize; self->dev = g_strdup(dev); self->name = self->dev + /* Shorter version for logging */ (g_str_has_prefix(self->dev, "/dev/") ? 5 : 0); if (gbinder_system_ioctl(fd, BINDER_SET_MAX_THREADS, &max_threads) < 0) { GERR("%s failed to set max threads (%u): %s", dev, max_threads, strerror(errno)); } /* Choose the protocol based on the device name * if none is explicitly specified */ self->protocol = protocol ? protocol : gbinder_rpc_protocol_for_device(dev); return self; } else { GERR("%s failed to mmap: %s", dev, strerror(errno)); } } } else { GERR("Can't get binder version from %s: %s", dev, strerror(errno)); } gbinder_system_close(fd); } else { GERR("Can't open %s: %s", dev, strerror(errno)); } return NULL; } GBinderDriver* gbinder_driver_ref( GBinderDriver* self) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); return self; } void gbinder_driver_unref( GBinderDriver* self) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_driver_close(self); g_free(self->dev); g_slice_free(GBinderDriver, self); } } void gbinder_driver_close( GBinderDriver* self) { if (self->vm) { GDEBUG("Closing %s", self->dev); gbinder_system_munmap(self->vm, self->vmsize); gbinder_system_close(self->fd); self->fd = -1; self->vm = NULL; self->vmsize = 0; } } int gbinder_driver_fd( GBinderDriver* self) { /* Only used by unit tests */ return self->fd; } int gbinder_driver_poll( GBinderDriver* self, struct pollfd* pipefd) { struct pollfd fds[2]; nfds_t n = 1; int err; memset(fds, 0, sizeof(fds)); fds[0].fd = self->fd; fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; if (pipefd) { fds[n].fd = pipefd->fd; fds[n].events = pipefd->events; n++; } err = poll(fds, n, -1); if (err >= 0) { if (pipefd) { pipefd->revents = fds[1].revents; } return fds[0].revents; } if (pipefd) { pipefd->revents = 0; } return err; } const char* gbinder_driver_dev( GBinderDriver* self) { return self->dev; } const GBinderIo* gbinder_driver_io( GBinderDriver* self) { return self->io; } const GBinderRpcProtocol* gbinder_driver_protocol( GBinderDriver* self) { return self->protocol; } gboolean gbinder_driver_acquire_done( GBinderDriver* self, GBinderLocalObject* obj) { GBinderIoBuf write; guint8 buf[4 + GBINDER_MAX_PTR_COOKIE_SIZE]; guint32* data = (guint32*)buf; const GBinderIo* io = self->io; data[0] = io->bc.acquire_done; memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)buf; write.size = 4 + io->encode_ptr_cookie(data + 1, obj); GVERBOSE("< BC_ACQUIRE_DONE %p", obj); return gbinder_driver_write(self, &write) >= 0; } gboolean gbinder_driver_dead_binder_done( GBinderDriver* self, GBinderRemoteObject* obj) { if (G_LIKELY(obj)) { GBinderIoBuf write; guint8 buf[4 + GBINDER_MAX_COOKIE_SIZE]; guint32* data = (guint32*)buf; const GBinderIo* io = self->io; data[0] = io->bc.dead_binder_done; memset(&write, 0, sizeof(write)); write.ptr = (uintptr_t)buf; write.size = 4 + io->encode_cookie(data + 1, obj->handle); GVERBOSE("< BC_DEAD_BINDER_DONE 0x%08x", obj->handle); return gbinder_driver_write(self, &write) >= 0; } else { return FALSE; } } gboolean gbinder_driver_request_death_notification( GBinderDriver* self, GBinderRemoteObject* obj) { if (G_LIKELY(obj)) { GVERBOSE("< BC_REQUEST_DEATH_NOTIFICATION 0x%08x", obj->handle); return gbinder_driver_handle_cookie(self, self->io->bc.request_death_notification, obj); } else { return FALSE; } } gboolean gbinder_driver_clear_death_notification( GBinderDriver* self, GBinderRemoteObject* obj) { if (G_LIKELY(obj)) { GVERBOSE("< BC_CLEAR_DEATH_NOTIFICATION 0x%08x", obj->handle); return gbinder_driver_handle_cookie(self, self->io->bc.clear_death_notification, obj); } else { return FALSE; } } gboolean gbinder_driver_increfs( GBinderDriver* self, guint32 handle) { GVERBOSE("< BC_INCREFS 0x%08x", handle); return gbinder_driver_cmd_int32(self, self->io->bc.increfs, handle); } gboolean gbinder_driver_decrefs( GBinderDriver* self, guint32 handle) { GVERBOSE("< BC_DECREFS 0x%08x", handle); return gbinder_driver_cmd_int32(self, self->io->bc.decrefs, handle); } gboolean gbinder_driver_acquire( GBinderDriver* self, guint32 handle) { GVERBOSE("< BC_ACQUIRE 0x%08x", handle); return gbinder_driver_cmd_int32(self, self->io->bc.acquire, handle); } gboolean gbinder_driver_release( GBinderDriver* self, guint32 handle) { GVERBOSE("< BC_RELEASE 0x%08x", handle); return gbinder_driver_cmd_int32(self, self->io->bc.release, handle); } void gbinder_driver_close_fds( GBinderDriver* self, void** objects, const void* end) { const GBinderIo* io = self->io; void** ptr; /* Caller checks objects for NULL */ for (ptr = objects; *ptr; ptr++) { void* obj = *ptr; GASSERT(obj < end); if (obj < end) { int fd; if (io->decode_fd_object(obj, (guint8*)end - (guint8*)obj, &fd)) { if (close(fd) < 0) { GWARN("Error closing fd %d: %s", fd, strerror(errno)); } } } } } void gbinder_driver_free_buffer( GBinderDriver* self, void* buffer) { if (buffer) { GBinderIoBuf write; const GBinderIo* io = self->io; guint8 wbuf[GBINDER_MAX_POINTER_SIZE + sizeof(guint32)]; guint32* cmd = (guint32*)wbuf; guint len = sizeof(*cmd); GVERBOSE("< BC_FREE_BUFFER %p", buffer); *cmd = io->bc.free_buffer; len += io->encode_pointer(wbuf + len, buffer); /* Write it */ write.ptr = (uintptr_t)wbuf; write.size = len; write.consumed = 0; (void) gbinder_driver_write(self, &write); } } gboolean gbinder_driver_enter_looper( GBinderDriver* self) { GVERBOSE("< BC_ENTER_LOOPER"); return gbinder_driver_cmd(self, self->io->bc.enter_looper); } gboolean gbinder_driver_exit_looper( GBinderDriver* self) { GVERBOSE("< BC_EXIT_LOOPER"); return gbinder_driver_cmd(self, self->io->bc.exit_looper); } int gbinder_driver_read( GBinderDriver* self, GBinderObjectRegistry* reg, GBinderHandler* handler) { GBinderDriverReadData read; GBinderDriverContext context; int ret; gbinder_driver_read_init(&read); gbinder_driver_context_init(&context, &read.buf, reg, handler); ret = gbinder_driver_write_read(self, NULL, context.rbuf); if (ret >= 0) { /* Loop until we have handled all the incoming commands */ gbinder_driver_handle_commands(self, &context); while (read.buf.io.consumed && gbinder_handler_can_loop(handler)) { ret = gbinder_driver_write_read(self, NULL, context.rbuf); if (ret >= 0) { gbinder_driver_handle_commands(self, &context); } else { break; } } } gbinder_driver_context_cleanup(&context); return ret; } int gbinder_driver_transact( GBinderDriver* self, GBinderObjectRegistry* reg, GBinderHandler* handler, guint32 handle, guint32 code, GBinderLocalRequest* req, GBinderRemoteReply* reply) { GBinderDriverReadData read; GBinderDriverContext context; GBinderIoBuf write; GBinderDriverReadBuf* rbuf = &read.buf; const GBinderIo* io = self->io; const guint flags = reply ? 0 : GBINDER_TX_FLAG_ONEWAY; GBinderOutputData* data = gbinder_local_request_data(req); const gsize extra_buffers = gbinder_output_data_buffers_size(data); GUtilIntArray* offsets = gbinder_output_data_offsets(data); void* offsets_buf = NULL; guint8 wbuf[GBINDER_MAX_BC_TRANSACTION_SG_SIZE + sizeof(guint32)]; guint32* cmd = (guint32*)wbuf; guint len = sizeof(*cmd); int txstatus = (-EAGAIN); gbinder_driver_read_init(&read); gbinder_driver_context_init(&context, &read.buf, reg, handler); /* Build BC_TRANSACTION */ if (extra_buffers) { GVERBOSE("< BC_TRANSACTION_SG 0x%08x 0x%08x %u bytes", handle, code, (guint)extra_buffers); gbinder_driver_verbose_dump_bytes(' ', data->bytes); *cmd = io->bc.transaction_sg; len += io->encode_transaction_sg(wbuf + len, handle, code, data->bytes, flags, offsets, &offsets_buf, extra_buffers); } else { GVERBOSE("< BC_TRANSACTION 0x%08x 0x%08x", handle, code); gbinder_driver_verbose_dump_bytes(' ', data->bytes); *cmd = io->bc.transaction; len += io->encode_transaction(wbuf + len, handle, code, data->bytes, flags, offsets, &offsets_buf); } #if 0 /* GUTIL_LOG_VERBOSE */ if (offsets && offsets->count) { gbinder_driver_verbose_dump('<', (uintptr_t)offsets_buf, offsets->count * io->pointer_size); } #endif /* GUTIL_LOG_VERBOSE */ /* Write it */ write.ptr = (uintptr_t)wbuf; write.size = len; write.consumed = 0; /* And wait for reply. Positive txstatus is the transaction status, * negative is a driver error (except for -EAGAIN meaning that there's * no status yet) */ while (txstatus == (-EAGAIN)) { int err = gbinder_driver_write_read(self, &write, rbuf); if (err < 0) { txstatus = err; } else { txstatus = gbinder_driver_txstatus(self, &context, reply); } } if (txstatus >= 0) { /* The whole thing should've been written in case of success */ GASSERT(write.consumed == write.size || txstatus > 0); /* Loop until we have handled all the incoming commands */ gbinder_driver_handle_commands(self, &context); while (rbuf->io.consumed) { int err = gbinder_driver_write_read(self, NULL, rbuf); if (err < 0) { txstatus = err; break; } else { gbinder_driver_handle_commands(self, &context); } } } gbinder_driver_context_cleanup(&context); g_free(offsets_buf); return txstatus; } GBinderLocalRequest* gbinder_driver_local_request_new( GBinderDriver* self, const char* iface) { return gbinder_local_request_new_iface(self->io, self->protocol, iface); } GBinderLocalRequest* gbinder_driver_local_request_new_ping( GBinderDriver* self) { GBinderWriter writer; GBinderLocalRequest* req = gbinder_local_request_new(self->io, self->protocol, NULL); gbinder_local_request_init_writer(req, &writer); self->protocol->write_ping(&writer); return req; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_driver.h000066400000000000000000000110671526127616700176520ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_DRIVER_H #define GBINDER_DRIVER_H #include "gbinder_types_p.h" struct pollfd; GBinderDriver* gbinder_driver_new( const char* dev, const GBinderRpcProtocol* protocol) GBINDER_INTERNAL; GBinderDriver* gbinder_driver_ref( GBinderDriver* driver) GBINDER_INTERNAL; void gbinder_driver_unref( GBinderDriver* driver) GBINDER_INTERNAL; void gbinder_driver_close( GBinderDriver* driver) GBINDER_INTERNAL; int gbinder_driver_fd( GBinderDriver* driver) GBINDER_INTERNAL; int gbinder_driver_poll( GBinderDriver* driver, struct pollfd* pollfd) GBINDER_INTERNAL; const char* gbinder_driver_dev( GBinderDriver* driver) GBINDER_INTERNAL; const GBinderIo* gbinder_driver_io( GBinderDriver* driver) GBINDER_INTERNAL; const GBinderRpcProtocol* gbinder_driver_protocol( GBinderDriver* driver) GBINDER_INTERNAL; gboolean gbinder_driver_acquire_done( GBinderDriver* driver, GBinderLocalObject* obj) GBINDER_INTERNAL; gboolean gbinder_driver_dead_binder_done( GBinderDriver* driver, GBinderRemoteObject* obj) GBINDER_INTERNAL; gboolean gbinder_driver_request_death_notification( GBinderDriver* driver, GBinderRemoteObject* obj) GBINDER_INTERNAL; gboolean gbinder_driver_clear_death_notification( GBinderDriver* driver, GBinderRemoteObject* obj) GBINDER_INTERNAL; gboolean gbinder_driver_increfs( GBinderDriver* driver, guint32 handle) GBINDER_INTERNAL; gboolean gbinder_driver_decrefs( GBinderDriver* driver, guint32 handle) GBINDER_INTERNAL; gboolean gbinder_driver_acquire( GBinderDriver* driver, guint32 handle) GBINDER_INTERNAL; gboolean gbinder_driver_release( GBinderDriver* driver, guint32 handle) GBINDER_INTERNAL; void gbinder_driver_close_fds( GBinderDriver* driver, void** objects, const void* end) GBINDER_INTERNAL; void gbinder_driver_free_buffer( GBinderDriver* driver, void* buffer) GBINDER_INTERNAL; gboolean gbinder_driver_enter_looper( GBinderDriver* driver) GBINDER_INTERNAL; gboolean gbinder_driver_exit_looper( GBinderDriver* driver) GBINDER_INTERNAL; int gbinder_driver_read( GBinderDriver* driver, GBinderObjectRegistry* reg, GBinderHandler* handler) GBINDER_INTERNAL; int gbinder_driver_transact( GBinderDriver* driver, GBinderObjectRegistry* reg, GBinderHandler* handler, guint32 handle, guint32 code, GBinderLocalRequest* request, GBinderRemoteReply* reply) GBINDER_INTERNAL; GBinderLocalRequest* gbinder_driver_local_request_new( GBinderDriver* driver, const char* iface) GBINDER_INTERNAL; GBinderLocalRequest* gbinder_driver_local_request_new_ping( GBinderDriver* self) GBINDER_INTERNAL; #endif /* GBINDER_DRIVER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_eventloop.c000066400000000000000000000235431526127616700203670ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Jolla Ltd. * Copyright (C) 2020-2021 Slava Monich * * 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_eventloop_p.h" #include typedef struct gbinder_idle_callback_data { GBinderEventLoopCallback* cb; GBinderEventLoopCallbackFunc func; GDestroyNotify destroy; gpointer data; } GBinderIdleCallbackData; #define GBINDER_DEFAULT_EVENTLOOP (&gbinder_eventloop_glib) static const GBinderEventLoopIntegration gbinder_eventloop_glib; /*==========================================================================* * GLib event loop integration *==========================================================================*/ typedef struct gbinder_eventloop_glib_timeout { GBinderEventLoopTimeout timeout; guint id; GSourceFunc func; gpointer data; } GBinderEventLoopTimeoutGLib; typedef struct gbinder_eventloop_glib_callback { GSource source; GBinderEventLoopCallback callback; } GBinderEventLoopCallbackGLib; static inline GBinderEventLoopTimeoutGLib* gbinder_eventloop_glib_timeout_cast( GBinderEventLoopTimeout* timeout) { return G_CAST(timeout,GBinderEventLoopTimeoutGLib,timeout); } static inline GSource* gbinder_eventloop_glib_callback_source( GBinderEventLoopCallback* callback) { return &(G_CAST(callback,GBinderEventLoopCallbackGLib,callback)->source); } static gboolean gbinder_eventloop_glib_timeout_callback( gpointer data) { GBinderEventLoopTimeoutGLib* timeout = data; return timeout->func(timeout->data); } static void gbinder_eventloop_glib_timeout_finalize( gpointer data) { g_slice_free1(sizeof(GBinderEventLoopTimeoutGLib), data); } static GBinderEventLoopTimeout* gbinder_eventloop_glib_timeout_add( guint interval, GSourceFunc func, gpointer data) { GBinderEventLoopTimeoutGLib* impl = g_slice_new(GBinderEventLoopTimeoutGLib); impl->timeout.eventloop = &gbinder_eventloop_glib; impl->func = func; impl->data = data; impl->id = g_timeout_add_full(G_PRIORITY_DEFAULT, interval, gbinder_eventloop_glib_timeout_callback, impl, gbinder_eventloop_glib_timeout_finalize); return &impl->timeout; } static void gbinder_eventloop_glib_timeout_remove( GBinderEventLoopTimeout* timeout) { g_source_remove(gbinder_eventloop_glib_timeout_cast(timeout)->id); } static gboolean gbinder_eventloop_glib_callback_prepare( GSource* source, gint* timeout) { *timeout = 0; return TRUE; } static gboolean gbinder_eventloop_glib_callback_check( GSource* source) { return TRUE; } static gboolean gbinder_eventloop_glib_callback_dispatch( GSource* source, GSourceFunc callback, gpointer user_data) { ((GBinderEventLoopCallbackFunc)callback)(user_data); return G_SOURCE_REMOVE; } static GBinderEventLoopCallback* gbinder_eventloop_glib_callback_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify finalize) { static GSourceFuncs callback_funcs = { gbinder_eventloop_glib_callback_prepare, gbinder_eventloop_glib_callback_check, gbinder_eventloop_glib_callback_dispatch }; GBinderEventLoopCallbackGLib* impl = (GBinderEventLoopCallbackGLib*) g_source_new(&callback_funcs, sizeof(GBinderEventLoopCallbackGLib)); impl->callback.eventloop = &gbinder_eventloop_glib; g_source_set_callback(&impl->source, (GSourceFunc) func, data, finalize); return &impl->callback; } static void gbinder_eventloop_glib_callback_ref( GBinderEventLoopCallback* cb) { g_source_ref(gbinder_eventloop_glib_callback_source(cb)); } static void gbinder_eventloop_glib_callback_unref( GBinderEventLoopCallback* cb) { g_source_unref(gbinder_eventloop_glib_callback_source(cb)); } static void gbinder_eventloop_glib_callback_schedule( GBinderEventLoopCallback* cb) { static GMainContext* context = NULL; if (!context) context = g_main_context_default(); g_source_attach(gbinder_eventloop_glib_callback_source(cb), context); } static void gbinder_eventloop_glib_callback_cancel( GBinderEventLoopCallback* cb) { g_source_destroy(gbinder_eventloop_glib_callback_source(cb)); } static void gbinder_eventloop_glib_cleanup( void) { } static const GBinderEventLoopIntegration gbinder_eventloop_glib = { gbinder_eventloop_glib_timeout_add, gbinder_eventloop_glib_timeout_remove, gbinder_eventloop_glib_callback_new, gbinder_eventloop_glib_callback_ref, gbinder_eventloop_glib_callback_unref, gbinder_eventloop_glib_callback_schedule, gbinder_eventloop_glib_callback_cancel, gbinder_eventloop_glib_cleanup }; /*==========================================================================* * Implementation *==========================================================================*/ static void gbinder_idle_callback_invoke_proc( void* user_data) { GBinderIdleCallbackData* idle = user_data; if (idle->func) { idle->func(idle->data); } gbinder_idle_callback_unref(idle->cb); } static void gbinder_idle_callback_invoke_done( void* user_data) { GBinderIdleCallbackData* idle = user_data; if (idle->destroy) { idle->destroy(idle->data); } gutil_slice_free(idle); } /*==========================================================================* * Internal interface *==========================================================================*/ static const GBinderEventLoopIntegration* gbinder_eventloop = GBINDER_DEFAULT_EVENTLOOP; GBinderEventLoopTimeout* gbinder_timeout_add( guint interval, GSourceFunc function, gpointer data) { return gbinder_eventloop->timeout_add(interval, function, data); } GBinderEventLoopTimeout* gbinder_idle_add( GSourceFunc function, gpointer data) { return gbinder_eventloop->timeout_add(0, function, data); } void gbinder_timeout_remove( GBinderEventLoopTimeout* timeout) { if (timeout) { timeout->eventloop->timeout_remove(timeout); } } GBinderEventLoopCallback* gbinder_idle_callback_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify finalize) { return gbinder_eventloop->callback_new(func, data, finalize); } GBinderEventLoopCallback* gbinder_idle_callback_schedule_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify finalize) { GBinderEventLoopCallback* cb = gbinder_eventloop->callback_new(func, data, finalize); gbinder_idle_callback_schedule(cb); return cb; } GBinderEventLoopCallback* gbinder_idle_callback_ref( GBinderEventLoopCallback* cb) { if (cb) { cb->eventloop->callback_ref(cb); return cb; } return NULL; } void gbinder_idle_callback_unref( GBinderEventLoopCallback* cb) { if (cb) { cb->eventloop->callback_unref(cb); } } void gbinder_idle_callback_schedule( GBinderEventLoopCallback* cb) { if (cb) { cb->eventloop->callback_schedule(cb); } } void gbinder_idle_callback_cancel( GBinderEventLoopCallback* cb) { if (cb) { cb->eventloop->callback_cancel(cb); } } void gbinder_idle_callback_destroy( GBinderEventLoopCallback* cb) { if (cb) { const GBinderEventLoopIntegration* eventloop = cb->eventloop; eventloop->callback_cancel(cb); eventloop->callback_unref(cb); } } /* Non-cancellable callback */ void gbinder_idle_callback_invoke_later( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify destroy) { GBinderIdleCallbackData* idle = g_slice_new(GBinderIdleCallbackData); idle->func = func; idle->data = data; idle->destroy = destroy; idle->cb = gbinder_idle_callback_new(gbinder_idle_callback_invoke_proc, idle, gbinder_idle_callback_invoke_done); gbinder_idle_callback_schedule(idle->cb); } /*==========================================================================* * Public interface *==========================================================================*/ void gbinder_eventloop_set( const GBinderEventLoopIntegration* loop) { if (!loop) loop = GBINDER_DEFAULT_EVENTLOOP; if (gbinder_eventloop != loop) { const GBinderEventLoopIntegration* prev = gbinder_eventloop; gbinder_eventloop = loop; prev->cleanup(); } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_eventloop_p.h000066400000000000000000000064771526127616700207220ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Jolla Ltd. * Copyright (C) 2020-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_EVENTLOOP_PRIVATE_H #define GBINDER_EVENTLOOP_PRIVATE_H #include "gbinder_types_p.h" #include "gbinder_eventloop.h" GBinderEventLoopTimeout* gbinder_timeout_add( guint millis, GSourceFunc func, gpointer data) G_GNUC_WARN_UNUSED_RESULT GBINDER_INTERNAL; GBinderEventLoopTimeout* gbinder_idle_add( GSourceFunc func, gpointer data) G_GNUC_WARN_UNUSED_RESULT GBINDER_INTERNAL; void gbinder_timeout_remove( GBinderEventLoopTimeout* timeout) GBINDER_INTERNAL; GBinderEventLoopCallback* gbinder_idle_callback_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify destroy) G_GNUC_WARN_UNUSED_RESULT GBINDER_INTERNAL; GBinderEventLoopCallback* gbinder_idle_callback_schedule_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify destroy) G_GNUC_WARN_UNUSED_RESULT GBINDER_INTERNAL; GBinderEventLoopCallback* gbinder_idle_callback_ref( GBinderEventLoopCallback* cb) GBINDER_INTERNAL; void gbinder_idle_callback_unref( GBinderEventLoopCallback* cb) GBINDER_INTERNAL; void gbinder_idle_callback_schedule( GBinderEventLoopCallback* cb) GBINDER_INTERNAL; void gbinder_idle_callback_cancel( GBinderEventLoopCallback* cb) GBINDER_INTERNAL; void gbinder_idle_callback_destroy( GBinderEventLoopCallback* cb) GBINDER_INTERNAL; void gbinder_idle_callback_invoke_later( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify destroy) GBINDER_INTERNAL; #endif /* GBINDER_EVENTLOOP_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_fmq.c000066400000000000000000000522031526127616700171320ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_fmq_p.h" #include "gbinder_log.h" #include #include #include #include #include #include #include #if GBINDER_FMQ_SUPPORTED /* Grantor data positions */ enum { READ_PTR_POS = 0, WRITE_PTR_POS, DATA_PTR_POS, EVENT_FLAG_PTR_POS }; typedef struct gbinder_fmq { GBinderMQDescriptor* desc; guint8* ring; guint64* read_ptr; guint64* write_ptr; guint32* event_flag_ptr; guint32 refcount; } GBinderFmq; GBINDER_INLINE_FUNC GBinderFmqGrantorDescriptor* gbinder_fmq_get_grantor_descriptor( GBinderFmq* self, gint index) { return (GBinderFmqGrantorDescriptor*)(self->desc->grantors.data.ptr) + index; } static gsize gbinder_fmq_available_to_read_bytes( GBinderFmq* self, gboolean contiguous) { const guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_ACQUIRE); const gsize available_total = __atomic_load_n(self->write_ptr, __ATOMIC_ACQUIRE) - read_ptr; if (contiguous) { /* * The number of bytes that can be read contiguously from * read offset without wrapping around the ring buffer. */ const gsize size = gbinder_fmq_get_grantor_descriptor(self, DATA_PTR_POS)->extent; const gsize available_contiguous = size - (read_ptr % size); return (available_contiguous < available_total) ? available_contiguous : available_total; } else { return available_total; } } static gsize gbinder_fmq_available_to_write_bytes( GBinderFmq* self, gboolean contiguous) { const guint32 size = gbinder_fmq_get_grantor_descriptor(self, DATA_PTR_POS)->extent; const gsize available_total = size - gbinder_fmq_available_to_read_bytes(self, FALSE); if (contiguous) { /* * The number of bytes that can be written contiguously starting from * write_offset without wrapping around the ring buffer. */ const guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_RELAXED); const gsize available_contiguous = size - (write_ptr % size); return (available_contiguous < available_total) ? available_contiguous : available_total; } else { return available_total; } } static GBinderFmqGrantorDescriptor* gbinder_fmq_create_grantors( gsize queue_size_bytes, gsize num_fds, gboolean configure_event_flag) { const gsize num_grantors = configure_event_flag ? (EVENT_FLAG_PTR_POS + 1) : (DATA_PTR_POS + 1); GBinderFmqGrantorDescriptor* grantors = g_new0(GBinderFmqGrantorDescriptor, num_grantors); gsize pos, offset; gsize mem_sizes[] = { sizeof(guint64), /* read pointer counter */ sizeof(guint64), /* write pointer counter */ queue_size_bytes, /* data buffer */ sizeof(guint32) /* event flag pointer */ }; for (pos = 0, offset = 0; pos < num_grantors; pos++) { GBinderFmqGrantorDescriptor* grantor = grantors + pos; guint32 grantor_fd_index; gsize grantor_offset; if (pos == DATA_PTR_POS && num_fds == 2) { grantor_fd_index = 1; grantor_offset = 0; } else { grantor_fd_index = 0; grantor_offset = offset; offset += mem_sizes[pos]; } grantor->fd_index = grantor_fd_index; grantor->offset = (guint32)G_ALIGN8(grantor_offset); grantor->extent = mem_sizes[pos]; } return grantors; } static void* gbinder_fmq_map_grantor_descriptor( GBinderFmq* self, guint32 index) { if (index < self->desc->grantors.count) { const GBinderFmqGrantorDescriptor* desc = gbinder_fmq_get_grantor_descriptor(self, index); /* Offset for mmap must be a multiple of PAGE_SIZE */ const guint32 map_offset = (desc->offset & ~(getpagesize()-1)); const guint32 map_length = desc->offset - map_offset + desc->extent; const GBinderFds* fds = self->desc->data.fds; void* address = mmap(0, map_length, PROT_READ | PROT_WRITE, MAP_SHARED, gbinder_fds_get_fd(fds, desc->fd_index), map_offset); if (address != MAP_FAILED) { return (guint8*)address + (desc->offset - map_offset); } else { GWARN("mmap failed: %d", errno); } } return NULL; } static void gbinder_fmq_unmap_grantor_descriptor( GBinderFmq* self, void* address, guint index) { if (index < self->desc->grantors.count && address) { const GBinderFmqGrantorDescriptor* desc = gbinder_fmq_get_grantor_descriptor(self, index); const gsize remainder = desc->offset & (getpagesize() - 1); munmap((guint8*)address - remainder, remainder + desc->extent); } } static void gbinder_fmq_free( GBinderFmq* self) { if (self->desc) { if (self->desc->flags == GBINDER_FMQ_TYPE_UNSYNC_WRITE) { g_free(self->read_ptr); } else { gbinder_fmq_unmap_grantor_descriptor(self, self->read_ptr, READ_PTR_POS); } gbinder_fmq_unmap_grantor_descriptor(self, self->write_ptr, WRITE_PTR_POS); gbinder_fmq_unmap_grantor_descriptor(self, self->ring, DATA_PTR_POS); gbinder_fmq_unmap_grantor_descriptor(self, self->event_flag_ptr, EVENT_FLAG_PTR_POS); g_free((GBinderFmqGrantorDescriptor*)self->desc->grantors.data.ptr); g_free((GBinderFds*)self->desc->data.fds); g_free(self->desc); } g_slice_free(GBinderFmq, self); } /* Private API */ GBinderMQDescriptor* gbinder_fmq_get_descriptor( const GBinderFmq* self) { return self->desc; } /* Public API */ GBinderFmq* gbinder_fmq_new( gsize item_size, gsize num_items, GBINDER_FMQ_TYPE type, GBINDER_FMQ_FLAGS flags, gint fd, gsize buffer_size) { if (item_size <= 0) { GWARN("Incorrect item size"); } else if (num_items <= 0) { GWARN("Empty queue requested"); } else if (num_items > SIZE_MAX / item_size) { GWARN("Requested message queue size too large"); } else if (fd != -1 && num_items * item_size > buffer_size) { GWARN("The size needed for items (%"G_GSIZE_FORMAT") is larger " "than the supplied buffer size (%"G_GSIZE_FORMAT")", num_items * item_size, buffer_size); } else { GBinderFmq* self = g_slice_new0(GBinderFmq); gboolean configure_event_flag = (flags & GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG) != 0; gsize queue_size_bytes = num_items * item_size; gsize meta_data_size; gsize shmem_size; int shmem_fd; meta_data_size = 2 * sizeof(guint64); if (configure_event_flag) { meta_data_size += sizeof(guint32); } /* Allocate shared memory */ if (fd != -1) { /* User-supplied ringbuffer memory provided, * allocating memory only for meta data */ shmem_size = (meta_data_size + getpagesize() - 1) & ~(getpagesize() - 1); } else { /* Allocate ringbuffer, read counter and write counter */ shmem_size = (G_ALIGN8(queue_size_bytes) + meta_data_size + getpagesize() - 1) & ~(getpagesize() - 1); } shmem_fd = syscall(__NR_memfd_create, "MessageQueue", MFD_CLOEXEC); if (shmem_fd >= 0 && ftruncate(shmem_fd, shmem_size) == 0) { GBinderFmqGrantorDescriptor* grantors; gsize num_fds = (fd != -1) ? 2 : 1; gsize fds_size = sizeof(GBinderFds) + sizeof(int) * num_fds; GBinderFds* fds = (GBinderFds*)g_malloc0(fds_size); fds->version = fds_size; fds->num_fds = num_fds; (((int*)((fds) + 1))[0]) = shmem_fd; if (fd != -1) { /* Use user-supplied file descriptor for fd_index 1 */ (((int*)((fds) + 1))[1]) = fd; } grantors = gbinder_fmq_create_grantors(queue_size_bytes, num_fds, configure_event_flag); /* Fill FMQ descriptor */ self->desc = g_new0(GBinderMQDescriptor, 1); self->desc->data.fds = fds; self->desc->quantum = item_size; self->desc->flags = type; self->desc->grantors.data.ptr = grantors; self->desc->grantors.count = configure_event_flag ? (EVENT_FLAG_PTR_POS + 1) : (DATA_PTR_POS + 1); self->desc->grantors.owns_buffer = TRUE; /* Initialize memory pointers */ if (type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) { self->read_ptr = gbinder_fmq_map_grantor_descriptor(self, READ_PTR_POS); } else { /* * Unsynchronized write FMQs may have multiple readers and * each reader would have their own read pointer counter. */ self->read_ptr = g_new0(guint64, 1); } if (!self->read_ptr) { GWARN("Read pointer is null"); } self->write_ptr = gbinder_fmq_map_grantor_descriptor(self, WRITE_PTR_POS); if (!self->write_ptr) { GWARN("Write pointer is null"); } if (!(flags & GBINDER_FMQ_FLAG_NO_RESET_POINTERS)) { __atomic_store_n(self->read_ptr, 0, __ATOMIC_RELEASE); __atomic_store_n(self->write_ptr, 0, __ATOMIC_RELEASE); } else if (type != GBINDER_FMQ_TYPE_SYNC_READ_WRITE) { /* Always reset the read pointer */ __atomic_store_n(self->read_ptr, 0, __ATOMIC_RELEASE); } self->ring = gbinder_fmq_map_grantor_descriptor(self, DATA_PTR_POS); if (!self->ring) { GWARN("Ring buffer pointer is null"); } if (self->desc->grantors.count > EVENT_FLAG_PTR_POS) { self->event_flag_ptr = gbinder_fmq_map_grantor_descriptor(self, EVENT_FLAG_PTR_POS); if (!self->event_flag_ptr) { GWARN("Event flag pointer is null"); } } g_atomic_int_set(&self->refcount, 1); return self; } GWARN("Failed to allocate shared memory: %s", strerror(errno)); gbinder_fmq_free(self); } return NULL; } GBinderFmq* gbinder_fmq_ref( GBinderFmq* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return self; } void gbinder_fmq_unref( GBinderFmq* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_fmq_free(self); } } } gsize gbinder_fmq_available_to_read( GBinderFmq* self) { return G_LIKELY(self) ? (gbinder_fmq_available_to_read_bytes(self, FALSE) / self->desc->quantum) : 0; } gsize gbinder_fmq_available_to_write( GBinderFmq* self) { return G_LIKELY(self) ? (gbinder_fmq_available_to_write_bytes(self, FALSE) / self->desc->quantum) : 0; } gsize gbinder_fmq_available_to_read_contiguous( GBinderFmq* self) { return G_LIKELY(self) ? (gbinder_fmq_available_to_read_bytes(self, TRUE) / self->desc->quantum) : 0; } gsize gbinder_fmq_available_to_write_contiguous( GBinderFmq* self) { return G_LIKELY(self) ? (gbinder_fmq_available_to_write_bytes(self, TRUE) / self->desc->quantum) : 0; } const void* gbinder_fmq_begin_read( GBinderFmq* self, gsize items) { void* ptr = NULL; if (G_LIKELY(self) && G_LIKELY(items > 0)) { gsize size = gbinder_fmq_get_grantor_descriptor(self, DATA_PTR_POS)->extent; gsize item_size = self->desc->quantum; gsize bytes_desired = items * item_size; guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_ACQUIRE); guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_RELAXED); if ((write_ptr % item_size) || (read_ptr % item_size)) { GWARN("Unable to write data because of misaligned pointer"); } else if (write_ptr - read_ptr > size) { __atomic_store_n(self->read_ptr, write_ptr, __ATOMIC_RELEASE); } else if (write_ptr - read_ptr < bytes_desired) { /* Not enough data to read in FMQ. */ } else { ptr = self->ring + (read_ptr % size); } } return ptr; } void* gbinder_fmq_begin_write( GBinderFmq* self, gsize items) { void* ptr = NULL; if (G_LIKELY(self) && G_LIKELY(items > 0)) { const gsize item_size = self->desc->quantum; const gsize size = gbinder_fmq_get_grantor_descriptor(self, DATA_PTR_POS)->extent; if ((self->desc->flags == GBINDER_FMQ_TYPE_SYNC_READ_WRITE && (gbinder_fmq_available_to_write(self) < items)) || items > size / item_size) { /* Incorrect parameters */ } else { guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_RELAXED); if (write_ptr % item_size) { GWARN("The write pointer has become misaligned."); } else { ptr = self->ring + (write_ptr % size); } } } return ptr; } void gbinder_fmq_end_read( GBinderFmq* self, gsize items) { if (G_LIKELY(self) && G_LIKELY(items > 0)) { gsize size = gbinder_fmq_get_grantor_descriptor(self, DATA_PTR_POS)->extent; guint64 read_ptr = __atomic_load_n(self->read_ptr, __ATOMIC_RELAXED); guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_ACQUIRE); /* * If queue type is unsynchronized, it is possible that a write * overflow may have occurred. */ if (write_ptr - read_ptr > size) { __atomic_store_n(self->read_ptr, write_ptr, __ATOMIC_RELEASE); } else { read_ptr += items * self->desc->quantum; __atomic_store_n(self->read_ptr, read_ptr, __ATOMIC_RELEASE); } } } void gbinder_fmq_end_write( GBinderFmq* self, gsize items) { if (G_LIKELY(self) && G_LIKELY(items > 0)) { guint64 write_ptr = __atomic_load_n(self->write_ptr, __ATOMIC_RELAXED); write_ptr += items * self->desc->quantum; __atomic_store_n(self->write_ptr, write_ptr, __ATOMIC_RELEASE); } } gboolean gbinder_fmq_read( GBinderFmq* self, void* data, gsize items) { if (G_LIKELY(self) && G_LIKELY(data) && G_LIKELY(items > 0)) { const void* in_data = gbinder_fmq_begin_read(self, items); if (in_data) { /* * The number of messages that can be read contiguously without * wrapping around the ring buffer. */ const gsize contiguous_messages = gbinder_fmq_available_to_read_contiguous(self); const gsize item_size = self->desc->quantum; if (contiguous_messages < items) { /* A wrap around is required */ memcpy(data, in_data, contiguous_messages * item_size); memcpy((char*)data + contiguous_messages * item_size, self->ring, (items - contiguous_messages) * item_size); } else { /* A wrap around is not required */ memcpy(data, in_data, items * item_size); } gbinder_fmq_end_read(self, items); return TRUE; } } return FALSE; } gboolean gbinder_fmq_write( GBinderFmq* self, const void* data, gsize items) { if (G_LIKELY(self) && G_LIKELY(data) && G_LIKELY(items > 0)) { void *out_data = gbinder_fmq_begin_write(self, items); if (out_data) { /* * The number of messages that can be written contiguously without * wrapping around the ring buffer. */ const gsize contiguous_messages = gbinder_fmq_available_to_write_contiguous(self); const gsize item_size = self->desc->quantum; if (contiguous_messages < items) { /* A wrap around is required. */ memcpy(out_data, data, contiguous_messages * item_size); memcpy(self->ring, (char *)data + contiguous_messages * item_size / sizeof(char), (items - contiguous_messages) * item_size); } else { /* A wrap around is not required to write items */ memcpy(out_data, data, items * item_size); } gbinder_fmq_end_write(self, items); return TRUE; } } return FALSE; } int gbinder_fmq_wait_timeout( GBinderFmq* self, guint32 bit_mask, guint32* state, int timeout_ms) { if (G_UNLIKELY(!state) || G_UNLIKELY(!self)) { return (-EINVAL); } else if (!self->event_flag_ptr) { return (-ENOSYS); } else if (!bit_mask) { return (-EINVAL); } else { guint32 old_value = __atomic_fetch_and(self->event_flag_ptr, ~bit_mask, __ATOMIC_SEQ_CST); guint32 set_bits = old_value & bit_mask; /* Check if any of the bits was already set */ if (set_bits != 0) { *state = set_bits; return 0; } else if (!timeout_ms) { return (-ETIMEDOUT); } else { int ret; if (timeout_ms > 0) { struct timespec deadline; const guint64 ms = 1000000; const guint64 sec = 1000 * ms; clock_gettime(CLOCK_MONOTONIC, &deadline); deadline.tv_sec += timeout_ms / 1000; deadline.tv_nsec += (timeout_ms % 1000) * ms; if (deadline.tv_nsec >= sec) { deadline.tv_sec++; deadline.tv_nsec -= sec; } ret = syscall(__NR_futex, self->event_flag_ptr, FUTEX_WAIT_BITSET, old_value, &deadline, NULL, bit_mask); } else { ret = syscall(__NR_futex, self->event_flag_ptr, FUTEX_WAIT_BITSET, old_value, NULL, NULL, bit_mask); } if (ret == -1) { return errno ? (-errno) : -EFAULT; } else { old_value = __atomic_fetch_and(self->event_flag_ptr, ~bit_mask, __ATOMIC_SEQ_CST); *state = old_value & bit_mask; return (*state) ? 0 : (-EAGAIN); } } } } int gbinder_fmq_wake( GBinderFmq* self, guint32 bit_mask) { int ret = 0; if (G_LIKELY(self)) { if (!self->event_flag_ptr) { /* Event flag is not configured */ ret = -ENOSYS; } else if (!bit_mask) { /* Ignore zero bit mask */ } else { /* Set bit mask only if needed */ guint32 old_value = __atomic_fetch_or(self->event_flag_ptr, bit_mask, __ATOMIC_SEQ_CST); if (~old_value & bit_mask) { ret = syscall(__NR_futex, self->event_flag_ptr, FUTEX_WAKE_BITSET, G_MAXUINT32, NULL, NULL, bit_mask); } if (ret == -1) { /* Report error code */ ret = -errno; } } } else { ret = -EINVAL; } return ret; } #else /* !GBINDER_FMQ_SUPPORTED */ #pragma message("Not compiling FMQ") #endif /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_fmq_p.h000066400000000000000000000066071526127616700174650ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_FMQ_PRIVATE_H #define GBINDER_FMQ_PRIVATE_H #include #include "gbinder_types_p.h" /* FMQ functionality requires __NR_memfd_create syscall */ #include #ifdef __NR_memfd_create # define GBINDER_FMQ_SUPPORTED 1 #else # define GBINDER_FMQ_SUPPORTED 0 #endif /* * From linux/memfd.h */ #ifndef MFD_CLOEXEC # define MFD_CLOEXEC 0x0001U #endif /* * FMQ types */ typedef struct gbinder_fmq_grantor_descriptor { guint32 flags GBINDER_ALIGNED(4); guint32 fd_index GBINDER_ALIGNED(4); guint32 offset GBINDER_ALIGNED(4); guint64 extent GBINDER_ALIGNED(8); } GBinderFmqGrantorDescriptor; G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, flags) == 0); G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, fd_index) == 4); G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, offset) == 8); G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderFmqGrantorDescriptor, extent) == 16); G_STATIC_ASSERT(sizeof(GBinderFmqGrantorDescriptor) == 24); typedef struct gbinder_mq_descriptor { GBinderHidlVec grantors; union { guint64 value; const GBinderFds* fds; } data; guint32 quantum; guint32 flags; } GBinderMQDescriptor; #define GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET (0) #define GBINDER_MQ_DESCRIPTOR_FDS_OFFSET (16) G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderMQDescriptor, grantors) == GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET); G_STATIC_ASSERT(G_STRUCT_OFFSET(GBinderMQDescriptor, data) == GBINDER_MQ_DESCRIPTOR_FDS_OFFSET); G_STATIC_ASSERT(sizeof(GBinderMQDescriptor) == 32); GBinderMQDescriptor* gbinder_fmq_get_descriptor( const GBinderFmq* self) GBINDER_INTERNAL; #endif /* GBINDER_FMQ_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_handler.h000066400000000000000000000052731526127616700177760ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_HANDLER_H #define GBINDER_HANDLER_H #include "gbinder_types_p.h" typedef struct gbinder_handler_functions { gboolean (*can_loop)(GBinderHandler* handler); GBinderLocalReply* (*transact)(GBinderHandler* handler, GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status); } GBinderHandlerFunctions; struct gbinder_handler { const GBinderHandlerFunctions* f; }; /* Inline wrappers */ GBINDER_INLINE_FUNC gboolean gbinder_handler_can_loop( GBinderHandler* self) { return self && self->f->can_loop && self->f->can_loop(self); } GBINDER_INLINE_FUNC GBinderLocalReply* gbinder_handler_transact( GBinderHandler* self, GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return self ? self->f->transact(self, obj, req, code, flags, status) : NULL; } #endif /* GBINDER_HANDLER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_io.c000066400000000000000000000511251526127616700167600ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * 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_io.h" #include "gbinder_buffer_p.h" #include "gbinder_local_object_p.h" #include "gbinder_remote_object_p.h" #include "gbinder_object_registry.h" #include "gbinder_rpc_protocol.h" #include "gbinder_writer.h" #include "gbinder_system.h" #include "gbinder_log.h" #include "binder.h" #include #include #include /* * This file is included from gbinder_io_32.c and gbinder_io_64.c to * generate the code for different ioctl codes and structure sizes. */ #define GBINDER_POINTER_SIZE sizeof(binder_uintptr_t) #define GBINDER_IO_FN__(prefix,suffix) prefix##_##suffix #define GBINDER_IO_FN_(prefix,suffix) GBINDER_IO_FN__(prefix,suffix) #define GBINDER_IO_FN(fn) GBINDER_IO_FN_(GBINDER_IO_PREFIX,fn) static int GBINDER_IO_FN(write_read)( int fd, GBinderIoBuf* write, GBinderIoBuf* read) { int ret; struct binder_write_read bwr; memset(&bwr, 0, sizeof(bwr)); if (write) { bwr.write_buffer = write->ptr + write->consumed; bwr.write_size = write->size - write->consumed; } if (read) { bwr.read_buffer = read->ptr + read->consumed; bwr.read_size = read->size - read->consumed; } ret = gbinder_system_ioctl(fd, BINDER_WRITE_READ, &bwr); if (ret >= 0) { if (write) { write->consumed += bwr.write_consumed; } if (read) { read->consumed += bwr.read_consumed; } } else { GERR("binder_write_read: %s", strerror(errno)); } return ret; } /* Returns size of the object */ static gsize GBINDER_IO_FN(object_size)( const void* obj, const GBinderRpcProtocol* protocol) { if (obj) { const struct binder_object_header* hdr = obj; switch (hdr->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: return sizeof(struct flat_binder_object) + protocol->flat_binder_object_extra; case BINDER_TYPE_FD: return sizeof(struct binder_fd_object); case BINDER_TYPE_FDA: return sizeof(struct binder_fd_array_object); case BINDER_TYPE_PTR: return sizeof(struct binder_buffer_object); } } return 0; } /* Returns size of the object's extra data */ static gsize GBINDER_IO_FN(object_data_size)( const void* obj) { if (obj) { const struct binder_object_header* hdr = obj; switch (hdr->type) { case BINDER_TYPE_PTR: return ((struct binder_buffer_object*)obj)->length; case BINDER_TYPE_FDA: return ((struct binder_fd_array_object*)obj)->num_fds * 4; } } return 0; } /* Writes pointer to the buffer */ static guint GBINDER_IO_FN(encode_pointer)( void* out, const void* pointer) { binder_uintptr_t* dest = out; *dest = (uintptr_t)pointer; return sizeof(*dest); } /* Writes cookie to the buffer */ static guint GBINDER_IO_FN(encode_cookie)( void* out, guint64 cookie) { binder_uintptr_t* dest = out; *dest = (uintptr_t)cookie; return sizeof(*dest); } /* Encodes flat_binder_object */ static guint GBINDER_IO_FN(encode_local_object)( void* out, GBinderLocalObject* obj, const GBinderRpcProtocol* protocol) { struct flat_binder_object* dest = out; memset(dest, 0, sizeof(*dest)); dest->hdr.type = BINDER_TYPE_BINDER; if (obj) { dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; dest->binder = (uintptr_t)obj; } if (protocol->finish_flatten_binder) { protocol->finish_flatten_binder(dest + 1, obj ? obj->stability : GBINDER_STABILITY_UNDECLARED); } else if (protocol->flat_binder_object_extra) { memset(dest + 1, 0, protocol->flat_binder_object_extra); } return sizeof(*dest) + protocol->flat_binder_object_extra; } static guint GBINDER_IO_FN(encode_remote_object)( void* out, GBinderRemoteObject* obj, const GBinderRpcProtocol* protocol) { struct flat_binder_object* dest = out; memset(dest, 0, sizeof(*dest)); if (obj) { dest->hdr.type = BINDER_TYPE_HANDLE; dest->flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; dest->handle = obj->handle; } else { dest->hdr.type = BINDER_TYPE_BINDER; } if (protocol->finish_flatten_binder) { protocol->finish_flatten_binder(dest + 1, obj ? obj->stability : GBINDER_STABILITY_UNDECLARED); } else if (protocol->flat_binder_object_extra) { memset(dest + 1, 0, protocol->flat_binder_object_extra); } return sizeof(*dest) + protocol->flat_binder_object_extra; } static guint GBINDER_IO_FN(encode_fd_object)( void* out, int fd) { struct binder_fd_object* dest = out; memset(dest, 0, sizeof(*dest)); dest->hdr.type = BINDER_TYPE_FD; dest->pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; dest->fd = fd; return sizeof(*dest); } static guint GBINDER_IO_FN(encode_fda_object)( void* out, const GBinderFds *fds, const GBinderParent* parent) { struct binder_fd_array_object* dest = out; memset(dest, 0, sizeof(*dest)); dest->hdr.type = BINDER_TYPE_FDA; dest->num_fds = fds->num_fds; dest->parent = parent->index; dest->parent_offset = parent->offset; return sizeof(*dest); } /* Encodes binder_buffer_object */ static guint GBINDER_IO_FN(encode_buffer_object)( void* out, const void* data, gsize size, const GBinderParent* parent) { struct binder_buffer_object* dest = out; memset(dest, 0, sizeof(*dest)); dest->hdr.type = BINDER_TYPE_PTR; dest->buffer = (uintptr_t)data; dest->length = size; if (parent) { dest->flags |= BINDER_BUFFER_FLAG_HAS_PARENT; dest->parent = parent->index; dest->parent_offset = parent->offset; } return sizeof(*dest); } static guint GBINDER_IO_FN(encode_handle_cookie)( void* out, GBinderRemoteObject* obj) { struct binder_handle_cookie* dest = out; /* We find the object by handle, so we use handle as a cookie */ dest->handle = obj->handle; dest->cookie = obj->handle; return sizeof(*dest); } static guint GBINDER_IO_FN(encode_ptr_cookie)( void* out, GBinderLocalObject* obj) { struct binder_ptr_cookie* dest = out; /* We never send these cookies and don't expect them back */ dest->ptr = (uintptr_t)obj; dest->cookie = 0; return sizeof(*dest); } /* Fills binder_transaction_data for BC_TRANSACTION/REPLY */ static void GBINDER_IO_FN(fill_transaction_data)( struct binder_transaction_data* tr, guint32 handle, guint32 code, const GByteArray* payload, guint tx_flags, GUtilIntArray* offsets, void** offsets_buf) { memset(tr, 0, sizeof(*tr)); tr->target.handle = handle; tr->code = code; tr->data_size = payload->len; tr->data.ptr.buffer = (uintptr_t)payload->data; tr->flags = tx_flags; if (offsets && offsets->count) { guint i; binder_size_t* tx_offsets = g_new(binder_size_t, offsets->count); tr->offsets_size = offsets->count * sizeof(binder_size_t); tr->data.ptr.offsets = (uintptr_t)tx_offsets; for (i = 0; i < offsets->count; i++) { tx_offsets[i] = offsets->data[i]; } *offsets_buf = tx_offsets; } else { *offsets_buf = NULL; } } /* Encodes BC_TRANSACTION data */ static guint GBINDER_IO_FN(encode_transaction)( void* out, guint32 handle, guint32 code, const GByteArray* payload, guint flags, GUtilIntArray* offsets, void** offsets_buf) { struct binder_transaction_data* tr = out; GBINDER_IO_FN(fill_transaction_data)(tr, handle, code, payload, (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS, offsets, offsets_buf); return sizeof(*tr); } /* Encodes BC_TRANSACTION_SG data */ static guint GBINDER_IO_FN(encode_transaction_sg)( void* out, guint32 handle, guint32 code, const GByteArray* payload, guint flags, GUtilIntArray* offsets, void** offsets_buf, gsize buffers_size) { struct binder_transaction_data_sg* sg = out; GBINDER_IO_FN(fill_transaction_data)(&sg->transaction_data, handle, code, payload, (flags & GBINDER_TX_FLAG_ONEWAY) ? TF_ONE_WAY : TF_ACCEPT_FDS, offsets, offsets_buf); /* The driver seems to require buffers to be 8-byte aligned */ sg->buffers_size = G_ALIGN8(buffers_size); return sizeof(*sg); } /* Encodes BC_REPLY data */ static guint GBINDER_IO_FN(encode_reply)( void* out, guint32 handle, guint32 code, const GByteArray* payload, GUtilIntArray* offsets, void** offsets_buf) { struct binder_transaction_data* tr = out; GBINDER_IO_FN(fill_transaction_data)(tr, handle, code, payload, 0, offsets, offsets_buf); return sizeof(*tr); } /* Encodes BC_REPLY_SG data */ static guint GBINDER_IO_FN(encode_reply_sg)( void* out, guint32 handle, guint32 code, const GByteArray* payload, GUtilIntArray* offsets, void** offsets_buf, gsize buffers_size) { struct binder_transaction_data_sg* sg = out; GBINDER_IO_FN(fill_transaction_data)(&sg->transaction_data, handle, code, payload, 0, offsets, offsets_buf); /* The driver seems to require buffers to be 8-byte aligned */ sg->buffers_size = G_ALIGN8(buffers_size); return sizeof(*sg); } /* Encode BC_REPLY with just status */ static guint GBINDER_IO_FN(encode_status_reply)( void* out, gint32* status) { struct binder_transaction_data* tr = out; memset(tr, 0, sizeof(*tr)); tr->flags = TF_STATUS_CODE; tr->data_size = sizeof(*status); tr->data.ptr.buffer = (uintptr_t)status; return sizeof(*tr); } /* Decode BR_REPLY and BR_TRANSACTION */ static void GBINDER_IO_FN(decode_transaction_data)( const void* data, GBinderIoTxData* tx) { const struct binder_transaction_data* tr = data; tx->objects = NULL; tx->code = tr->code; tx->flags = 0; tx->pid = tr->sender_pid; tx->euid = tr->sender_euid; tx->target = (void*)(uintptr_t)tr->target.ptr; tx->data = (void*)(uintptr_t)tr->data.ptr.buffer; if (tr->flags & TF_STATUS_CODE) { GASSERT(tr->data_size == 4); tx->status = *((gint32*)tx->data); tx->size = 0; } else { guint objcount = tr->offsets_size/sizeof(binder_size_t); const binder_size_t* offsets = (void*)(uintptr_t)tr->data.ptr.offsets; tx->status = GBINDER_STATUS_OK; tx->size = tr->data_size; if (tr->flags & TF_ONE_WAY) { tx->flags |= GBINDER_TX_FLAG_ONEWAY; } if (objcount > 0) { binder_size_t min_offset = 0; guint i; /* Validate the offsets */ for (i = 0; i < objcount; i++) { if (offsets[i] < min_offset || (offsets[i] + sizeof(struct flat_binder_object)) > tx->size) { GWARN("Invalid offset"); objcount = 0; break; } min_offset = offsets[i] + sizeof(struct flat_binder_object); } if (objcount > 0) { tx->objects = g_new(void*, objcount + 1); for (i = 0; i < objcount; i++) { tx->objects[i] = (guint8*)tx->data + offsets[i]; } tx->objects[objcount] = NULL; } } } } /* Decode binder_uintptr_t */ static guint GBINDER_IO_FN(decode_cookie)( const void* data, guint64* cookie) { const binder_uintptr_t* ptr = data; if (cookie) *cookie = *ptr; return sizeof(*ptr); } /* Decode struct binder_ptr_cookie */ static void* GBINDER_IO_FN(decode_ptr_cookie)( const void* data) { const struct binder_ptr_cookie* ptr = data; /* We never send cookie and don't expect it back */ GASSERT(!ptr->cookie); return (void*)(uintptr_t)ptr->ptr; } static guint GBINDER_IO_FN(decode_binder_handle)( const void* data, guint32* handle, const GBinderRpcProtocol* protocol) { const struct flat_binder_object* obj = data; /* Caller guarantees that data points to an object */ if (obj->hdr.type == BINDER_TYPE_HANDLE) { if (handle) { *handle = obj->handle; } return sizeof(*obj) + protocol->flat_binder_object_extra; } return 0; } static void* GBINDER_IO_FN(decode_binder_local)( const void* data) { const struct flat_binder_object* obj = data; /* Caller guarantees that data points to an object */ return (obj->hdr.type == BINDER_TYPE_BINDER) ? (void*)(uintptr_t)obj->binder : NULL; } static guint GBINDER_IO_FN(decode_binder_object)( const void* data, gsize size, GBinderObjectRegistry* reg, GBinderRemoteObject** out, const GBinderRpcProtocol* protocol) { const struct flat_binder_object* obj = data; if (size >= sizeof(struct binder_object_header)) { const gsize objsize = GBINDER_IO_FN(object_size)(obj, protocol); if (!objsize || objsize > size) { return 0; } switch (obj->hdr.type) { case BINDER_TYPE_HANDLE: if (out) { *out = gbinder_object_registry_get_remote(reg, obj->handle, REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE); if (*out && protocol->finish_unflatten_binder) { protocol->finish_unflatten_binder(obj + 1, *out); } } return objsize; case BINDER_TYPE_BINDER: if (!obj->binder) { /* That's a NULL reference */ if (out) { *out = NULL; } return objsize; } /* fallthrough */ default: GERR("Unsupported binder object type 0x%08x", obj->hdr.type); break; } } if (out) *out = NULL; return 0; } static guint GBINDER_IO_FN(decode_buffer_object)( GBinderBuffer* buf, gsize offset, GBinderIoBufferObject* out) { const void* data = (guint8*)buf->data + offset; const gsize size = (offset < buf->size) ? (buf->size - offset) : 0; const struct binder_buffer_object* flat = data; if (size >= sizeof(*flat) && flat->hdr.type == BINDER_TYPE_PTR) { if (out) { out->data = (void*)(uintptr_t)flat->buffer; out->size = (gsize)flat->length; out->parent_offset = (gsize)flat->parent_offset; out->has_parent = (flat->flags & BINDER_BUFFER_FLAG_HAS_PARENT) ? TRUE : FALSE; } return sizeof(*flat); } return 0; } static guint GBINDER_IO_FN(decode_fd_object)( const void* data, gsize size, int* fd) { const struct flat_binder_object* obj = data; if (size >= sizeof(*obj)) { switch (obj->hdr.type) { case BINDER_TYPE_FD: if (fd) *fd = obj->handle; return sizeof(*obj); default: break; } } if (fd) *fd = -1; return 0; } const GBinderIo GBINDER_IO_PREFIX = { .version = BINDER_CURRENT_PROTOCOL_VERSION, .pointer_size = GBINDER_POINTER_SIZE, /* Driver command protocol */ .bc = { .transaction = BC_TRANSACTION, .reply = BC_REPLY, .acquire_result = BC_ACQUIRE_RESULT, .free_buffer = BC_FREE_BUFFER, .increfs = BC_INCREFS, .acquire = BC_ACQUIRE, .release = BC_RELEASE, .decrefs = BC_DECREFS, .increfs_done = BC_INCREFS_DONE, .acquire_done = BC_ACQUIRE_DONE, .attempt_acquire = BC_ATTEMPT_ACQUIRE, .register_looper = BC_REGISTER_LOOPER, .enter_looper = BC_ENTER_LOOPER, .exit_looper = BC_EXIT_LOOPER, .request_death_notification = BC_REQUEST_DEATH_NOTIFICATION, .clear_death_notification = BC_CLEAR_DEATH_NOTIFICATION, .dead_binder_done = BC_DEAD_BINDER_DONE, .transaction_sg = BC_TRANSACTION_SG, .reply_sg = BC_REPLY_SG }, /* Driver return protocol */ .br = { .error = BR_ERROR, .ok = BR_OK, .transaction = BR_TRANSACTION, .reply = BR_REPLY, .acquire_result = BR_ACQUIRE_RESULT, .dead_reply = BR_DEAD_REPLY, .transaction_complete = BR_TRANSACTION_COMPLETE, .increfs = BR_INCREFS, .acquire = BR_ACQUIRE, .release = BR_RELEASE, .decrefs = BR_DECREFS, .attempt_acquire = BR_ATTEMPT_ACQUIRE, .noop = BR_NOOP, .spawn_looper = BR_SPAWN_LOOPER, .finished = BR_FINISHED, .dead_binder = BR_DEAD_BINDER, .clear_death_notification_done = BR_CLEAR_DEATH_NOTIFICATION_DONE, .failed_reply = BR_FAILED_REPLY }, .object_size = GBINDER_IO_FN(object_size), .object_data_size = GBINDER_IO_FN(object_data_size), /* Encoders */ .encode_pointer = GBINDER_IO_FN(encode_pointer), .encode_cookie = GBINDER_IO_FN(encode_cookie), .encode_local_object = GBINDER_IO_FN(encode_local_object), .encode_remote_object = GBINDER_IO_FN(encode_remote_object), .encode_fd_object = GBINDER_IO_FN(encode_fd_object), .encode_fda_object = GBINDER_IO_FN(encode_fda_object), .encode_buffer_object = GBINDER_IO_FN(encode_buffer_object), .encode_handle_cookie = GBINDER_IO_FN(encode_handle_cookie), .encode_ptr_cookie = GBINDER_IO_FN(encode_ptr_cookie), .encode_transaction = GBINDER_IO_FN(encode_transaction), .encode_transaction_sg = GBINDER_IO_FN(encode_transaction_sg), .encode_reply = GBINDER_IO_FN(encode_reply), .encode_reply_sg = GBINDER_IO_FN(encode_reply_sg), .encode_status_reply = GBINDER_IO_FN(encode_status_reply), /* Decoders */ .decode_transaction_data = GBINDER_IO_FN(decode_transaction_data), .decode_cookie = GBINDER_IO_FN(decode_cookie), .decode_ptr_cookie = GBINDER_IO_FN(decode_ptr_cookie), .decode_binder_handle = GBINDER_IO_FN(decode_binder_handle), .decode_binder_local = GBINDER_IO_FN(decode_binder_local), .decode_binder_object = GBINDER_IO_FN(decode_binder_object), .decode_buffer_object = GBINDER_IO_FN(decode_buffer_object), .decode_fd_object = GBINDER_IO_FN(decode_fd_object), /* ioctl wrappers */ .write_read = GBINDER_IO_FN(write_read) }; /* Compile time constraints */ G_STATIC_ASSERT(GBINDER_POINTER_SIZE <= GBINDER_MAX_POINTER_SIZE); G_STATIC_ASSERT(sizeof(struct flat_binder_object) <= GBINDER_MAX_BINDER_OBJECT_SIZE); G_STATIC_ASSERT(sizeof(struct binder_fd_object) <= GBINDER_MAX_BUFFER_OBJECT_SIZE); G_STATIC_ASSERT(sizeof(struct binder_fd_array_object) <= GBINDER_MAX_BUFFER_OBJECT_SIZE); G_STATIC_ASSERT(sizeof(struct binder_buffer_object) <= GBINDER_MAX_BUFFER_OBJECT_SIZE); G_STATIC_ASSERT(sizeof(struct binder_handle_cookie) <= GBINDER_MAX_HANDLE_COOKIE_SIZE); G_STATIC_ASSERT(sizeof(struct binder_transaction_data) <= GBINDER_MAX_BC_TRANSACTION_SIZE); G_STATIC_ASSERT(sizeof(struct binder_transaction_data_sg) <= GBINDER_MAX_BC_TRANSACTION_SG_SIZE); /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_io.h000066400000000000000000000175361526127616700167750ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_IO_H #define GBINDER_IO_H #include "gbinder_types_p.h" #include typedef struct gbinder_io_buf { uintptr_t ptr; gsize size; gsize consumed; } GBinderIoBuf; typedef struct gbinder_io_buffer_object { void* data; gsize size; gsize parent_offset; gboolean has_parent; } GBinderIoBufferObject; typedef struct gbinder_io_tx_data { int status; guint32 code; guint32 flags; /* GBINDER_TX_FLAG_xxx */ pid_t pid; uid_t euid; void* target; void* data; gsize size; void** objects; } GBinderIoTxData; /* Read buffer size (allocated on stack, shouldn't be too large) */ #define GBINDER_IO_READ_BUFFER_SIZE (128) /* * There are (at least) 2 versions of the binder ioctl API, implemented by * 32-bit and 64-bit kernels. The ioctl codes, transaction commands - many * of those are derived from the sizes of the structures being passed * between the driver and the user space client. All these differences * are abstracted away by GBinderIo interfaces. * * The API version is returned by BINDER_VERSION ioctl which itself doesn't * depend on the API version (it would be very strange if it did). */ struct gbinder_io { int version; guint pointer_size; /* Driver command protocol */ struct gbinder_io_command_codes { guint transaction; guint reply; guint acquire_result; guint free_buffer; guint increfs; guint acquire; guint release; guint decrefs; guint increfs_done; guint acquire_done; guint attempt_acquire; guint register_looper; guint enter_looper; guint exit_looper; guint request_death_notification; guint clear_death_notification; guint dead_binder_done; guint transaction_sg; guint reply_sg; } bc; /* Driver return protocol */ struct gbinder_io_return_codes { guint error; guint ok; guint transaction; guint reply; guint acquire_result; guint dead_reply; guint transaction_complete; guint increfs; guint acquire; guint release; guint decrefs; guint attempt_acquire; guint noop; guint spawn_looper; guint finished; guint dead_binder; guint clear_death_notification_done; guint failed_reply; } br; /* Size of the object and its extra data */ gsize (*object_size)(const void* obj, const GBinderRpcProtocol* protocol); gsize (*object_data_size)(const void* obj); /* Writes pointer to the buffer. The destination buffer must have * at least GBINDER_IO_MAX_POINTER_SIZE bytes available. The * actual size is returned. */ #define GBINDER_MAX_POINTER_SIZE (8) guint (*encode_pointer)(void* out, const void* pointer); /* Writes cookie to the buffer. The destination buffer must have * at least GBINDER_IO_MAX_COOKIE_SIZE bytes available. The * actual size is returned. */ #define GBINDER_MAX_COOKIE_SIZE GBINDER_MAX_POINTER_SIZE guint (*encode_cookie)(void* out, guint64 cookie); /* Encode flat_buffer_object */ #define GBINDER_MAX_BINDER_OBJECT_SIZE (28) guint (*encode_local_object)(void* out, GBinderLocalObject* obj, const GBinderRpcProtocol* protocol); guint (*encode_remote_object)(void* out, GBinderRemoteObject* obj, const GBinderRpcProtocol* protocol); guint (*encode_fd_object)(void* out, int fd); guint (*encode_fda_object)(void* out, const GBinderFds *fds, const GBinderParent* parent); /* Encode binder_buffer_object */ #define GBINDER_MAX_BUFFER_OBJECT_SIZE (40) guint (*encode_buffer_object)(void* out, const void* data, gsize size, const GBinderParent* parent); /* Encode binder_handle_cookie */ #define GBINDER_MAX_HANDLE_COOKIE_SIZE (12) guint (*encode_handle_cookie)(void* out, GBinderRemoteObject* obj); /* Encode binder_ptr_cookie */ #define GBINDER_MAX_PTR_COOKIE_SIZE (16) guint (*encode_ptr_cookie)(void* out, GBinderLocalObject* obj); /* Encode BC_TRANSACTION/BC_TRANSACTION_SG data */ #define GBINDER_MAX_BC_TRANSACTION_SIZE (64) guint (*encode_transaction)(void* out, guint32 handle, guint32 code, const GByteArray* data, guint flags /* See below */, GUtilIntArray* offsets, void** offsets_buf); #define GBINDER_MAX_BC_TRANSACTION_SG_SIZE (72) guint (*encode_transaction_sg)(void* out, guint32 handle, guint32 code, const GByteArray* data, guint flags /* GBINDER_TX_FLAG_xxx */, GUtilIntArray* offsets, void** offsets_buf, gsize buffers_size); /* Encode BC_REPLY/REPLY_SG data */ #define GBINDER_MAX_BC_REPLY_SIZE GBINDER_MAX_BC_TRANSACTION_SIZE guint (*encode_reply)(void* out, guint32 handle, guint32 code, const GByteArray* data, GUtilIntArray* offsets, void** offsets_buf); #define GBINDER_MAX_BC_REPLY_SG_SIZE GBINDER_MAX_BC_TRANSACTION_SG_SIZE guint (*encode_reply_sg)(void* out, guint32 handle, guint32 code, const GByteArray* data, GUtilIntArray* offsets, void** offsets_buf, gsize buffers_size); /* Encode BC_REPLY */ guint (*encode_status_reply)(void* out, gint32* status); /* Decoders */ void (*decode_transaction_data)(const void* data, GBinderIoTxData* tx); void* (*decode_ptr_cookie)(const void* data); guint (*decode_cookie)(const void* data, guint64* cookie); guint (*decode_binder_handle)(const void* obj, guint32* handle, const GBinderRpcProtocol* protocol); void* (*decode_binder_local)(const void* obj); guint (*decode_binder_object)(const void* data, gsize size, GBinderObjectRegistry* reg, GBinderRemoteObject** obj, const GBinderRpcProtocol* protocol); guint (*decode_buffer_object)(GBinderBuffer* buf, gsize offset, GBinderIoBufferObject* out); guint (*decode_fd_object)(const void* data, gsize size, int* fd); /* ioctl wrappers */ int (*write_read)(int fd, GBinderIoBuf* write, GBinderIoBuf* read); }; extern const GBinderIo gbinder_io_32 GBINDER_INTERNAL; extern const GBinderIo gbinder_io_64 GBINDER_INTERNAL; #endif /* GBINDER_IO_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_io_32.c000066400000000000000000000035231526127616700172630ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 name of Jolla Ltd nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. */ #define BINDER_IPC_32BIT #define GBINDER_IO_PREFIX gbinder_io_32 #include "gbinder_io.c" /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_io_64.c000066400000000000000000000035221526127616700172670ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 name of Jolla Ltd nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. */ #undef BINDER_IPC_32BIT #define GBINDER_IO_PREFIX gbinder_io_64 #include "gbinder_io.c" /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_ipc.c000066400000000000000000001763541526127616700171400ustar00rootroot00000000000000/* * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE /* pthread_*_np */ #include "gbinder_ipc.h" #include "gbinder_driver.h" #include "gbinder_handler.h" #include "gbinder_io.h" #include "gbinder_rpc_protocol.h" #include "gbinder_object_registry.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_local_request_p.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_reply_p.h" #include "gbinder_remote_request_p.h" #include "gbinder_eventloop_p.h" #include "gbinder_writer.h" #include "gbinder_log.h" #include #include #include #include #include #include typedef struct gbinder_ipc_looper GBinderIpcLooper; typedef GObjectClass GBinderIpcClass; struct gbinder_ipc_priv { GBinderIpc* self; GThreadPool* tx_pool; GHashTable* tx_table; char* dev; char* key; const char* name; GBinderObjectRegistry object_registry; GMutex remote_objects_mutex; GHashTable* remote_objects; GMutex local_objects_mutex; GHashTable* local_objects; GMutex looper_mutex; GBinderIpcLooper* primary_loopers; GBinderIpcLooper* blocked_loopers; }; #define PARENT_CLASS gbinder_ipc_parent_class #define THIS_TYPE gbinder_ipc_get_type() #define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, GBinderIpc) GType THIS_TYPE GBINDER_INTERNAL; G_DEFINE_TYPE(GBinderIpc, gbinder_ipc, G_TYPE_OBJECT) /* * Binder requests are blocking, worker threads are needed in order to * implement asynchronous requests, hence the synchronization. */ static GHashTable* gbinder_ipc_table = NULL; static pthread_mutex_t gbinder_ipc_mutex = PTHREAD_MUTEX_INITIALIZER; #define GBINDER_IPC_MAX_TX_THREADS (15) #define GBINDER_IPC_MAX_PRIMARY_LOOPERS (5) #define GBINDER_IPC_LOOPER_START_TIMEOUT_SEC (2) #define GBINDER_IPC_LOOPER_JOIN_TIMEOUT_MS (500) /* * When looper receives the transaction: * * 1. Finds the target object and allocates GBinderIpcLooperTx. * 2. Posts the GBinderIpcLooperTx reference to the main thread * 4. Waits for (any) byte from the receiving end of the tx pipe. * * When the main thread receives GBinderIpcLooperTx: * * 1. Lets the object to process it and produce the response (GBinderOutput). * 2. Writes one byte (TX_DONE) to the sending end of the tx pipe. * 3. Unreferences GBinderIpcLooperTx * * When tx pipe wakes up the looper: * * 1. Sends the transaction to the kernel. * 2. Unreferences GBinderIpcLooperTx * * Note that GBinderIpcLooperTx can be deallocated on either looper or * main thread, depending on whether looper gives up on the transaction * before it gets processed. * * When transaction is blocked by gbinder_remote_request_block() call, it * gets slightly more complicated. Then the main thread writes TX_BLOCKED * to the pipe (rather than TX_DONE) and then looper thread spawn another * looper and keeps waiting for TX_DONE. */ #define TX_DONE (0x2a) #define TX_BLOCKED (0x3b) typedef enum gbinder_ipc_looper_tx_state { GBINDER_IPC_LOOPER_TX_SCHEDULED, GBINDER_IPC_LOOPER_TX_PROCESSING, GBINDER_IPC_LOOPER_TX_PROCESSED, GBINDER_IPC_LOOPER_TX_BLOCKING, GBINDER_IPC_LOOPER_TX_BLOCKED, GBINDER_IPC_LOOPER_TX_COMPLETE } GBINDER_IPC_LOOPER_TX_STATE; struct gbinder_ipc_looper_tx { /* Reference count */ gint refcount; /* These are filled by the looper: */ int pipefd[2]; guint32 code; guint32 flags; GBinderLocalObject* obj; GBinderRemoteRequest* req; /* And these by the main thread processing the transaction: */ GBINDER_IPC_LOOPER_TX_STATE state; GBinderLocalReply* reply; int status; } /* GBinderIpcLooperTx */; struct gbinder_ipc_looper { gint refcount; GBinderIpcLooper* next; char* name; GBinderHandler handler; GBinderDriver* driver; GBinderIpc* ipc; /* Not a reference! */ pthread_t thread; GMutex mutex; GCond start_cond; gint exit; gint started; gint joined; int pipefd[2]; int txfd[2]; }; typedef struct gbinder_ipc_tx_handler { int pipefd[2]; int txfd[2]; } GBinderIpcTxHandler; typedef struct gbinder_ipc_tx_priv GBinderIpcTxPriv; typedef void (*GBinderIpcTxPrivFunc)( GBinderIpcTxPriv* tx); typedef struct gbinder_ipc_tx_priv { GBinderIpcTx pub; GBinderIpcTxPrivFunc fn_exec; GBinderIpcTxPrivFunc fn_done; GBinderIpcTxPrivFunc fn_free; GBinderEventLoopCallback* completion; } GBinderIpcTxPriv; typedef struct gbinder_ipc_tx_internal { GBinderIpcTxPriv tx; guint32 handle; guint32 code; guint32 flags; int status; GBinderLocalRequest* req; GBinderRemoteReply* reply; GBinderIpcReplyFunc fn_reply; GDestroyNotify fn_destroy; } GBinderIpcTxInternal; typedef struct gbinder_ipc_tx_custom { GBinderIpcTxPriv tx; GBinderIpcTxFunc fn_custom_exec; GBinderIpcTxFunc fn_custom_done; GDestroyNotify fn_custom_destroy; } GBinderIpcTxCustom; static GBinderIpcLooper* gbinder_ipc_looper_new( GBinderIpc* ipc); static GBinderRemoteReply* gbinder_ipc_transact_sync_reply_worker( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req, int* status); static int gbinder_ipc_transact_sync_oneway_worker( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req); /*==========================================================================* * Utilities *==========================================================================*/ static gboolean gbinder_ipc_wait( int fd_wakeup, int fd_read, guint8* out) { struct pollfd fds[2]; memset(fds, 0, sizeof(fds)); fds[0].fd = fd_wakeup; fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; fds[1].fd = fd_read; fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; if (poll(fds, 2, -1) < 0) { GWARN("Transaction pipe polling error: %s", strerror(errno)); } else if (fds[1].revents & POLLIN) { const ssize_t n = read(fds[1].fd, out, 1); if (n == 1) { return TRUE; } else if (n < 0) { GWARN("Transaction pipe read error: %s", strerror(errno)); } else { GWARN("Nothing was read from the transaction pipe"); } } return FALSE; } static char* gbinder_ipc_make_key( const char* dev, const char* protocol) { return g_strdup_printf("%s:%s", protocol, dev); } /*==========================================================================* * GBinderIpcLooperTx *==========================================================================*/ static GBinderIpcLooperTx* gbinder_ipc_looper_tx_new( GBinderLocalObject* obj, guint32 code, guint32 flags, GBinderRemoteRequest* req, const int* fd) { GBinderIpcLooperTx* tx = g_slice_new0(GBinderIpcLooperTx); g_atomic_int_set(&tx->refcount, 1); memcpy(tx->pipefd, fd, sizeof(tx->pipefd)); tx->code = code; tx->flags = flags; tx->obj = gbinder_local_object_ref(obj); tx->req = gbinder_remote_request_ref(req); return tx; } static void gbinder_ipc_looper_tx_free( GBinderIpcLooperTx* tx) { if (tx->pipefd[0] >= 0) { close(tx->pipefd[0]); close(tx->pipefd[1]); } gbinder_local_object_unref(tx->obj); gbinder_remote_request_unref(tx->req); gbinder_local_reply_unref(tx->reply); g_slice_free(GBinderIpcLooperTx, tx); } static GBinderIpcLooperTx* gbinder_ipc_looper_tx_ref( GBinderIpcLooperTx* tx) { GASSERT(tx->refcount > 0); g_atomic_int_inc(&tx->refcount); return tx; } static gboolean gbinder_ipc_looper_tx_unref( GBinderIpcLooperTx* tx, gboolean dropfd) { gboolean dropped = FALSE; GASSERT(tx->refcount > 0); if (g_atomic_int_dec_and_test(&tx->refcount)) { if (dropfd) { tx->pipefd[0] = tx->pipefd[1] = -1; dropped = TRUE; } gbinder_ipc_looper_tx_free(tx); } return dropped; } /*==========================================================================* * State machine of transaction handling. All this is happening on the event * thread and therefore doesn't need to be synchronized. * * SCHEDULED * ========= * | * PROCESSING * ========== * | * --------------------- handler is called --------------------------------- * | * +---------------- request doesn't need to be blocked ----------+ * | | * gbinder_remote_request_block() | * | | * BLOCKING -- gbinder_remote_request_complete() --> PROCESSED | * ======== ========= | * | | | * --------------------- handler returns ----------------------------------- * | | | * BLOCKED COMPLETE <-------+ * ======= ======== * ^ * ... | * gbinder_remote_request_complete() is called later ----+ *==========================================================================*/ void gbinder_remote_request_block( GBinderRemoteRequest* req) /* Since 1.0.20 */ { if (G_LIKELY(req)) { GBinderIpcLooperTx* tx = req->tx; GASSERT(tx); if (G_LIKELY(tx)) { GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING); if (tx->state == GBINDER_IPC_LOOPER_TX_PROCESSING) { tx->state = GBINDER_IPC_LOOPER_TX_BLOCKING; } } } } void gbinder_remote_request_complete( GBinderRemoteRequest* req, GBinderLocalReply* reply, int status) /* Since 1.0.20 */ { if (G_LIKELY(req)) { GBinderIpcLooperTx* tx = req->tx; GASSERT(tx); if (G_LIKELY(tx)) { const guint8 done = TX_DONE; switch (tx->state) { case GBINDER_IPC_LOOPER_TX_BLOCKING: /* Called by the transaction handler */ tx->status = status; tx->reply = gbinder_local_reply_ref(reply); tx->state = GBINDER_IPC_LOOPER_TX_PROCESSED; break; case GBINDER_IPC_LOOPER_TX_BLOCKED: /* Really asynchronous completion */ tx->status = status; tx->reply = gbinder_local_reply_ref(reply); tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE; /* Wake up the looper */ if (write(tx->pipefd[1], &done, sizeof(done)) <= 0) { GWARN("Failed to wake up the looper"); } break; default: GWARN("Unexpected state %d in request completion", tx->state); break; } /* Clear the transaction reference */ gbinder_ipc_looper_tx_unref(tx, FALSE); req->tx = NULL; } } } /*==========================================================================* * GBinderIpcLooper *==========================================================================*/ static void gbinder_ipc_looper_free( GBinderIpcLooper* looper) { if (!looper->joined && looper->thread != pthread_self()) { pthread_join(looper->thread, NULL); } close(looper->pipefd[0]); close(looper->pipefd[1]); if (looper->txfd[0] >= 0) { close(looper->txfd[0]); close(looper->txfd[1]); } gbinder_driver_unref(looper->driver); g_free(looper->name); g_cond_clear(&looper->start_cond); g_mutex_clear(&looper->mutex); g_slice_free(GBinderIpcLooper, looper); } static GBinderIpcLooper* gbinder_ipc_looper_ref( GBinderIpcLooper* looper) { GASSERT(looper->refcount > 0); g_atomic_int_inc(&looper->refcount); return looper; } static void gbinder_ipc_looper_unref( GBinderIpcLooper* looper) { GASSERT(looper->refcount > 0); if (g_atomic_int_dec_and_test(&looper->refcount)) { gbinder_ipc_looper_free(looper); } } static void gbinder_ipc_looper_tx_handle( gpointer data) { GBinderIpcLooperTx* tx = data; GBinderRemoteRequest* req = tx->req; GBinderLocalReply* reply; int status = GBINDER_STATUS_OK; guint8 done; /* * Transaction reference for gbinder_remote_request_block() * and gbinder_remote_request_complete(). */ req->tx = gbinder_ipc_looper_tx_ref(tx); /* See state machine */ GASSERT(tx->state == GBINDER_IPC_LOOPER_TX_SCHEDULED); tx->state = GBINDER_IPC_LOOPER_TX_PROCESSING; /* Actually handle the transaction */ reply = gbinder_local_object_handle_transaction(tx->obj, req, tx->code, tx->flags, &status); /* Handle all possible return states */ switch (tx->state) { case GBINDER_IPC_LOOPER_TX_PROCESSING: /* Result was returned by the handler */ tx->reply = reply; tx->status = status; tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE; reply = NULL; break; case GBINDER_IPC_LOOPER_TX_PROCESSED: /* Result has been provided to gbinder_remote_request_complete() */ tx->state = GBINDER_IPC_LOOPER_TX_COMPLETE; break; case GBINDER_IPC_LOOPER_TX_BLOCKING: /* Result will be provided to gbinder_remote_request_complete() */ tx->state = GBINDER_IPC_LOOPER_TX_BLOCKED; break; default: break; } /* In case handler returns a reply which it wasn't expected to return */ GASSERT(!reply); gbinder_local_reply_unref(reply); /* Drop the transaction reference unless blocked */ if (tx->state == GBINDER_IPC_LOOPER_TX_BLOCKED) { done = TX_BLOCKED; /* * From this point on, it's GBinderRemoteRequest who's holding * reference to GBinderIpcLooperTx, not the other way around and * not both ways. Even if gbinder_remote_request_complete() never * gets called, transaction will still be completed when the last * reference to GBinderRemoteRequest goes away. And if request * never gets deallocated... oh well. */ gbinder_remote_request_unref(tx->req); tx->req = NULL; } else { done = TX_DONE; if (req->tx) { gbinder_ipc_looper_tx_unref(req->tx, FALSE); req->tx = NULL; } } /* And wake up the looper */ if (write(tx->pipefd[1], &done, sizeof(done)) <= 0) { GWARN("Failed to wake up the looper"); } } static void gbinder_ipc_looper_tx_done( gpointer data) { gbinder_ipc_looper_tx_unref(data, FALSE); } static void gbinder_ipc_looper_start( GBinderIpcLooper* looper) { if (!g_atomic_int_get(&looper->started)) { /* Lock */ g_mutex_lock(&looper->mutex); if (!g_atomic_int_get(&looper->started)) { g_cond_wait_until(&looper->start_cond, &looper->mutex, g_get_monotonic_time() + GBINDER_IPC_LOOPER_START_TIMEOUT_SEC * G_TIME_SPAN_SECOND); GASSERT(g_atomic_int_get(&looper->started)); } g_mutex_unlock(&looper->mutex); /* Unlock */ } } static gboolean gbinder_ipc_looper_remove_from_list( GBinderIpcLooper* looper, GBinderIpcLooper** list) { /* Caller holds looper_mutex */ if (*list) { if ((*list) == looper) { (*list) = looper->next; looper->next = NULL; return TRUE; } else { GBinderIpcLooper* prev = (*list); while (prev->next) { if (prev->next == looper) { prev->next = looper->next; looper->next = NULL; return TRUE; } prev = prev->next; } } } return FALSE; } static gboolean gbinder_ipc_looper_remove_primary( GBinderIpcLooper* looper) { return gbinder_ipc_looper_remove_from_list(looper, &looper->ipc->priv->primary_loopers); } static gboolean gbinder_ipc_looper_remove_blocked( GBinderIpcLooper* looper) { return gbinder_ipc_looper_remove_from_list(looper, &looper->ipc->priv->blocked_loopers); } static guint gbinder_ipc_looper_count_primary( GBinderIpcLooper* looper) { const GBinderIpcLooper* ptr = looper->ipc->priv->primary_loopers; guint n = 0; for (n = 0; ptr; ptr = ptr->next) n++; return n; } static gboolean gbinder_ipc_looper_can_loop( GBinderHandler* handler) { GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler); return !g_atomic_int_get(&looper->exit); } static GBinderLocalReply* gbinder_ipc_looper_transact( GBinderHandler* handler, GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* result) { GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler); GBinderIpc* ipc = looper->ipc; GBinderLocalReply* reply = NULL; int status = -EFAULT; if (looper->txfd[0] < 0 && pipe(looper->txfd)) { GERR("Failed to create a tx pipe: %s", strerror(errno)); } if (looper->txfd[0] >= 0) { GBinderIpcLooperTx* tx = gbinder_ipc_looper_tx_new(obj, code, flags, req, looper->txfd); GBinderIpcPriv* priv = ipc->priv; guint8 done = 0; gboolean was_blocked = FALSE; /* Let GBinderLocalObject handle the transaction on the main thread */ GBinderEventLoopCallback* callback = gbinder_idle_callback_schedule_new(gbinder_ipc_looper_tx_handle, gbinder_ipc_looper_tx_ref(tx), gbinder_ipc_looper_tx_done); /* Wait for either transaction completion or looper shutdown */ if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done) && done == TX_BLOCKED) { /* * We are going to block this looper for potentially * significant period of time. Start new looper to * accept normal incoming requests and terminate this * one when we are done with this transaction. * * For the duration of the transaction, this looper is * moved to the blocked_loopers list. */ GBinderIpcPriv* priv = looper->ipc->priv; GBinderIpcLooper* new_looper = NULL; /* Lock */ g_mutex_lock(&priv->looper_mutex); if (gbinder_ipc_looper_remove_primary(looper)) { GVERBOSE("Primary looper %s is blocked", looper->name); looper->next = priv->blocked_loopers; priv->blocked_loopers = looper; was_blocked = TRUE; /* If there's no more primary loopers left, create one */ if (!priv->primary_loopers) { new_looper = gbinder_ipc_looper_new(ipc); if (new_looper) { /* Will unref it after it gets started */ gbinder_ipc_looper_ref(new_looper); priv->primary_loopers = new_looper; } } } g_mutex_unlock(&priv->looper_mutex); /* Unlock */ if (new_looper) { /* Wait until it gets started */ gbinder_ipc_looper_start(new_looper); gbinder_ipc_looper_unref(new_looper); } /* Block until asynchronous transaction gets completed. */ done = 0; if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done)) { GVERBOSE("Looper %s is released", looper->name); GASSERT(done == TX_DONE); } } if (done) { GASSERT(done == TX_DONE); reply = gbinder_local_reply_ref(tx->reply); status = tx->status; } if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) { /* * This wasn't the last reference meaning that * gbinder_ipc_looper_tx_free() will close the * descriptors and we will have to create a new * pipe for the next transaction. */ looper->txfd[0] = looper->txfd[1] = -1; } gbinder_idle_callback_destroy(callback); if (was_blocked) { guint n; g_mutex_lock(&priv->looper_mutex); n = gbinder_ipc_looper_count_primary(looper); if (n >= GBINDER_IPC_MAX_PRIMARY_LOOPERS) { /* Looper will exit once transaction completes */ GDEBUG("Too many primary loopers (%u)", n); g_atomic_int_set(&looper->exit, 1); } else { /* Move it back to the primary list */ gbinder_ipc_looper_remove_blocked(looper); looper->next = priv->primary_loopers; priv->primary_loopers = looper; } g_mutex_unlock(&priv->looper_mutex); } } *result = status; return reply; } static gpointer gbinder_ipc_looper_thread( gpointer data) { GBinderIpcLooper* looper = data; GBinderDriver* driver = looper->driver; g_mutex_lock(&looper->mutex); pthread_setname_np(looper->thread, looper->name); if (gbinder_driver_enter_looper(driver)) { struct pollfd pipefd; int res; GDEBUG("Looper %s running", looper->name); g_atomic_int_set(&looper->started, TRUE); g_cond_broadcast(&looper->start_cond); g_mutex_unlock(&looper->mutex); memset(&pipefd, 0, sizeof(pipefd)); pipefd.fd = looper->pipefd[0]; /* read end of the pipe */ pipefd.events = POLLIN | POLLERR | POLLHUP | POLLNVAL; res = gbinder_driver_poll(driver, &pipefd); while (!g_atomic_int_get(&looper->exit) && ((res & POLLIN) || !res)) { if (res & POLLIN) { /* * No need to synchronize access to looper->ipc because * the other thread would wait until this thread exits * before setting looper->ipc to NULL. */ GBinderIpc* ipc = gbinder_ipc_ref(looper->ipc); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); /* But that gbinder_driver_read() may unref GBinderIpc */ int ret = gbinder_driver_read(driver, reg, &looper->handler); /* And this gbinder_ipc_unref() may release the last ref: */ gbinder_ipc_unref(ipc); /* And at this point looper->ipc may be NULL */ if (ret < 0) { GDEBUG("Looper %s failed", looper->name); break; } } /* Any event from this pipe terminates the loop */ if (pipefd.revents || g_atomic_int_get(&looper->exit)) { GDEBUG("Looper %s is requested to exit", looper->name); break; } res = gbinder_driver_poll(driver, &pipefd); } gbinder_driver_exit_looper(driver); /* * Again, there's no need to synchronize access to looper->ipc * because the other thread would wait until this thread exits * before setting looper->ipc to NULL */ if (looper->ipc) { GBinderIpcPriv* priv = looper->ipc->priv; /* Lock */ g_mutex_lock(&priv->looper_mutex); if (gbinder_ipc_looper_remove_blocked(looper) || gbinder_ipc_looper_remove_primary(looper)) { /* Spontaneous exit */ GDEBUG("Looper %s exits", looper->name); gbinder_ipc_looper_unref(looper); } else { /* Main thread is shutting it down */ GDEBUG("Looper %s done", looper->name); } g_mutex_unlock(&priv->looper_mutex); /* Unlock */ } else { GDEBUG("Looper %s is abandoned", looper->name); } } else { g_atomic_int_set(&looper->started, TRUE); g_cond_broadcast(&looper->start_cond); g_mutex_unlock(&looper->mutex); } gbinder_ipc_looper_unref(looper); return NULL; } static GBinderIpcLooper* gbinder_ipc_looper_new( GBinderIpc* ipc) { int fd[2]; /* Note: this call can actually fail */ if (!pipe(fd)) { static const GBinderHandlerFunctions handler_functions = { .can_loop = gbinder_ipc_looper_can_loop, .transact = gbinder_ipc_looper_transact }; GBinderIpcLooper* looper = g_slice_new0(GBinderIpcLooper); static gint gbinder_ipc_next_looper_id = 1; guint id = (guint)g_atomic_int_add(&gbinder_ipc_next_looper_id, 1); memcpy(looper->pipefd, fd, sizeof(fd)); looper->txfd[0] = looper->txfd[1] = -1; g_atomic_int_set(&looper->refcount, 1); g_cond_init(&looper->start_cond); g_mutex_init(&looper->mutex); g_mutex_lock(&looper->mutex); looper->name = g_strdup_printf("%s#%u", gbinder_ipc_name(ipc), id); looper->handler.f = &handler_functions; looper->ipc = ipc; looper->driver = gbinder_driver_ref(ipc->driver); if (!pthread_create(&looper->thread, NULL, gbinder_ipc_looper_thread, looper)) { /* gbinder_ipc_looper_thread() will release this reference: */ gbinder_ipc_looper_ref(looper); g_mutex_unlock(&looper->mutex); GDEBUG("Starting looper %s", looper->name); return looper; } else { GERR("Failed to create looper thread %s", looper->name); } g_mutex_unlock(&looper->mutex); gbinder_ipc_looper_unref(looper); } else { GERR("Failed to create looper pipe: %s", strerror(errno)); } return NULL; } void gbinder_ipc_looper_check( GBinderIpc* self) { if (G_LIKELY(self)) { GBinderIpcPriv* priv = self->priv; GBinderIpcLooper* new_looper = NULL; /* Lock */ g_mutex_lock(&priv->looper_mutex); if (!priv->primary_loopers) { priv->primary_loopers = gbinder_ipc_looper_new(self); new_looper = priv->primary_loopers; if (new_looper) { gbinder_ipc_looper_ref(new_looper); } } g_mutex_unlock(&priv->looper_mutex); /* Unlock */ /* We are not ready to accept incoming transactions until * looper has started. We may need to wait a bit. */ if (new_looper) { gbinder_ipc_looper_start(new_looper); gbinder_ipc_looper_unref(new_looper); } } } static void gbinder_ipc_looper_stop( GBinderIpcLooper* looper) { /* Caller checks looper for NULL */ if (looper->thread) { GDEBUG("Stopping looper %s", looper->name); g_atomic_int_set(&looper->exit, TRUE); if (looper->thread != pthread_self()) { guint8 done = TX_DONE; if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) { GWARN("Failed to stop looper %s", looper->name); } } } } static GBinderIpcLooper* gbinder_ipc_looper_stop_all( GBinderIpcLooper* loopers, GBinderIpcLooper* list) { while (list) { GBinderIpcLooper* looper = list; GBinderIpcLooper* next = looper->next; gbinder_ipc_looper_stop(looper); looper->next = loopers; loopers = looper; list = next; } return loopers; } static void gbinder_ipc_looper_join( GBinderIpcLooper* looper) { /* Caller checks looper for NULL */ if (looper->thread && looper->thread != pthread_self()) { struct timespec ts; int err = clock_gettime(CLOCK_REALTIME, &ts); if (!err) { const long ms = 1000000; const long sec = 1000 * ms; const long ns = ts.tv_nsec + GBINDER_IPC_LOOPER_JOIN_TIMEOUT_MS*ms; ts.tv_sec += ns / sec; ts.tv_nsec = ns % sec; err = pthread_timedjoin_np(looper->thread, NULL, &ts); } if (err) { /* Assume that looper is stuck in read */ GBinderIpc* ipc = looper->ipc; GBinderIpcPriv* priv = ipc->priv; GDEBUG("Looper %s is stuck", looper->name); /* Lock */ g_mutex_lock(&priv->looper_mutex); gbinder_driver_close(ipc->driver); g_mutex_unlock(&priv->looper_mutex); /* Unlock */ pthread_join(looper->thread, NULL); } looper->joined = TRUE; } looper->ipc = NULL; } /*==========================================================================* * GBinderIpcTxHandler * * It's needed to handle the following scenario: * * 1. Asynchronous call is made. The actual transaction is performed on * gbinder_ipc_tx_proc thread. * 2. While we were are waiting for completion of our transaction, we * receive a valid incoming transation. * 3. This transaction is handled by gbinder_ipc_tx_handler_transact. * * This seems to be quite a rare scenario, so we allocate a new * GBinderIpcTxHandler (and new pipes) for each such transaction, * to keep things as simple as possible. * *==========================================================================*/ static GBinderIpcTxHandler* gbinder_ipc_tx_handler_new( void) { GBinderIpcTxHandler* h = g_slice_new0(GBinderIpcTxHandler); /* Note: pipe() calls can actually fail */ if (!pipe(h->txfd)) { if (!pipe(h->pipefd)) { return h; } else { GERR("Failed to create a tx pipe: %s", strerror(errno)); } close(h->txfd[0]); close(h->txfd[1]); } else { GERR("Failed to create a tx pipe: %s", strerror(errno)); } g_slice_free(GBinderIpcTxHandler, h); return NULL; } static void gbinder_ipc_tx_handler_free( GBinderIpcTxHandler* h) { close(h->pipefd[0]); close(h->pipefd[1]); if (h->txfd[0] >= 0) { close(h->txfd[0]); close(h->txfd[1]); } g_slice_free(GBinderIpcTxHandler, h); } static GBinderLocalReply* gbinder_ipc_tx_handler_transact( GBinderHandler* handler, GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* result) { GBinderIpcTxHandler* h = gbinder_ipc_tx_handler_new(); GBinderLocalReply* reply = NULL; int status = -EFAULT; if (h) { GBinderIpcLooperTx* tx = gbinder_ipc_looper_tx_new(obj, code, flags, req, h->txfd); guint8 done = 0; /* Handle transaction on the main thread */ GBinderEventLoopCallback* callback = gbinder_idle_callback_schedule_new(gbinder_ipc_looper_tx_handle, gbinder_ipc_looper_tx_ref(tx), gbinder_ipc_looper_tx_done); /* Wait for completion */ if (gbinder_ipc_wait(h->pipefd[0], tx->pipefd[0], &done) && done == TX_BLOCKED) { /* Block until asynchronous transaction gets completed. */ done = 0; if (gbinder_ipc_wait(h->pipefd[0], tx->pipefd[0], &done)) { GASSERT(done == TX_DONE); } } if (done) { GASSERT(done == TX_DONE); reply = gbinder_local_reply_ref(tx->reply); status = tx->status; } if (!gbinder_ipc_looper_tx_unref(tx, TRUE)) { /* * This wasn't the last references meaning that * gbinder_ipc_looper_tx_free() will close the * descriptors and we will have to create a new * pipe for the next transaction. */ h->txfd[0] = h->txfd[1] = -1; } gbinder_idle_callback_destroy(callback); gbinder_ipc_tx_handler_free(h); } *result = status; return reply; } /*==========================================================================* * GBinderObjectRegistry *==========================================================================*/ static void gbinder_ipc_invalidate_local_object_locked( GBinderIpc* self, GBinderLocalObject* obj) { GBinderIpcPriv* priv = self->priv; if (priv->local_objects && g_hash_table_remove(priv->local_objects, obj)) { GVERBOSE_("%p %s", obj, gbinder_ipc_name(self)); if (g_hash_table_size(priv->local_objects) == 0) { g_hash_table_unref(priv->local_objects); priv->local_objects = NULL; } } } static void gbinder_ipc_invalidate_remote_handle_locked( GBinderIpc* self, guint32 handle) { GBinderIpcPriv* priv = self->priv; /* Caller holds priv->remote_objects_mutex */ if (priv->remote_objects) { const gpointer key = GINT_TO_POINTER(handle); #if GUTIL_LOG_VERBOSE const gpointer obj = g_hash_table_lookup(priv->remote_objects, key); #endif if (g_hash_table_remove(priv->remote_objects, key)) { GVERBOSE_("handle %u %p %s", handle, obj, gbinder_ipc_name(self)); if (g_hash_table_size(priv->remote_objects) == 0) { g_hash_table_unref(priv->remote_objects); priv->remote_objects = NULL; } } } } void gbinder_ipc_invalidate_local_object( GBinderIpc* self, GBinderLocalObject* obj) { GBinderIpcPriv* priv = self->priv; /* Lock */ g_mutex_lock(&priv->local_objects_mutex); gbinder_ipc_invalidate_local_object_locked(self, obj); g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ } void gbinder_ipc_invalidate_remote_handle( GBinderIpc* self, guint32 handle) { GBinderIpcPriv* priv = self->priv; /* Lock */ g_mutex_lock(&priv->remote_objects_mutex); gbinder_ipc_invalidate_remote_handle_locked(self, handle); g_mutex_unlock(&priv->remote_objects_mutex); /* Unlock */ } /** * Internal functions called by gbinder_object_dispose(). Among other things, * it means that it doesn't have to check GBinderIpc pointer for NULL. * * Note the following scenario (where object may be either local or remote): * * 1. Last reference to GBinderObject goes away. * 2. gbinder_ipc_object_disposed() is invoked by gbinder_object_dispose() * 3. Before gbinder_ipc_object_disposed() grabs the lock, * gbinder_ipc_new_remote_object() gets there first, finds the * object in the hashtable, bumps its refcount (under the lock) * and returns new reference to the caller. * 4. gbinder_ipc_object_disposed() finally gets its lock, finds * that the object's refcount is greater than zero and leaves * the object in the table. * * It's OK for a GObject to get re-referenced in dispose. glib will * recheck the refcount once dispose returns, the object stays alive * and gbinder_object_finalize() won't be called this time around, */ void gbinder_ipc_local_object_disposed( GBinderIpc* self, GBinderLocalObject* obj) { GBinderIpcPriv* priv = self->priv; /* Lock */ g_mutex_lock(&priv->local_objects_mutex); if (g_atomic_int_get(&obj->object.ref_count) == 1) { gbinder_ipc_invalidate_local_object_locked(self, obj); } g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ } void gbinder_ipc_remote_object_disposed( GBinderIpc* self, GBinderRemoteObject* obj) { GBinderIpcPriv* priv = self->priv; /* * Check of ref_count for 1 makes it possible (albeit quite unlikely) * that GBinderRemoteObject still remains in remote_objects table * when it's being finalized. * * For this to happen, other thread must re-reference GBinderRemoteObject * right before we grab the lock here (making ref_count greater than 1) * and then release that reference before g_object_unref() re-checks the * refcount. * * That's why another gbinder_ipc_invalidate_remote_handle() call from * gbinder_remote_object_finalize() is necessary to make sure that stale * object pointer isn't stored in the hashtable. * * We still have to invalidate the handle here because it's the last * point when GObject can be legitimately re-referenced and brought * back to life. Which means that GBinderIpc mutex has to acquired * twice during GBinderRemoteObject destruction. * * The same applies to GBinderLocalObject too, except that it calls * gbinder_ipc_invalidate_local_object() from its finalize() handler. */ /* Lock */ g_mutex_lock(&priv->remote_objects_mutex); if (g_atomic_int_get(&obj->object.ref_count) == 1) { gbinder_ipc_invalidate_remote_handle_locked(self, obj->handle); } g_mutex_unlock(&priv->remote_objects_mutex); /* Unlock */ } void gbinder_ipc_register_local_object( GBinderIpc* self, GBinderLocalObject* obj) { GBinderIpcPriv* priv = self->priv; /* Lock */ g_mutex_lock(&priv->local_objects_mutex); if (!priv->local_objects) { priv->local_objects = g_hash_table_new(g_direct_hash, g_direct_equal); } if (!g_hash_table_contains(priv->local_objects, obj)) { g_hash_table_insert(priv->local_objects, obj, obj); GVERBOSE_("%p %s", obj, gbinder_ipc_name(self)); } g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ gbinder_ipc_looper_check(self); } static GBinderLocalObject* gbinder_ipc_priv_get_local_object( GBinderIpcPriv* priv, void* pointer) { GBinderLocalObject* obj = NULL; if (pointer) { /* Lock */ g_mutex_lock(&priv->local_objects_mutex); if (priv->local_objects) { obj = g_hash_table_lookup(priv->local_objects, pointer); if (obj) { gbinder_local_object_ref(obj); } else { GWARN("Unknown local object %p %s", pointer, priv->name); } } else { GWARN("Unknown local object %p %s", pointer, priv->name); } g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ } return obj; } static GBinderRemoteObject* gbinder_ipc_priv_get_remote_object( GBinderIpcPriv* priv, guint32 handle, REMOTE_REGISTRY_CREATE create, gboolean maybe_dead) { GBinderRemoteObject* obj = NULL; void* key = GINT_TO_POINTER(handle); /* Lock */ g_mutex_lock(&priv->remote_objects_mutex); if (priv->remote_objects) { obj = g_hash_table_lookup(priv->remote_objects, key); } if (obj) { gbinder_remote_object_ref(obj); } else if (create != REMOTE_REGISTRY_DONT_CREATE) { GBinderIpc* self = priv->self; /* * If maybe_dead is TRUE, the caller is supposed to try reanimating * the object on the main thread not holding any global locks. */ obj = gbinder_remote_object_new(self, handle, maybe_dead ? REMOTE_OBJECT_CREATE_DEAD : (create == REMOTE_REGISTRY_CAN_CREATE) ? REMOTE_OBJECT_CREATE_ALIVE : REMOTE_OBJECT_CREATE_ACQUIRED); if (!priv->remote_objects) { priv->remote_objects = g_hash_table_new (g_direct_hash, g_direct_equal); } GVERBOSE_("%p handle %u %s", obj, handle, gbinder_ipc_name(self)); g_hash_table_replace(priv->remote_objects, key, obj); } else { GWARN("Unknown handle %u %s", handle, priv->name); } g_mutex_unlock(&priv->remote_objects_mutex); /* Unlock */ return obj; } GBinderLocalObject* gbinder_ipc_find_local_object( GBinderIpc* self, GBinderIpcLocalObjectCheckFunc func, void* user_data) { GBinderLocalObject* found = NULL; if (self) { GBinderIpcPriv* priv = self->priv; /* Lock */ g_mutex_lock(&priv->local_objects_mutex); if (priv->local_objects) { GHashTableIter it; gpointer value; g_hash_table_iter_init(&it, priv->local_objects); while (g_hash_table_iter_next(&it, NULL, &value)) { GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(value); if (func(obj, user_data)) { found = gbinder_local_object_ref(obj); break; } } } g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ } return found; } GBinderRemoteObject* gbinder_ipc_get_service_manager( GBinderIpc* self) { /* GBinderServiceManager makes sure that GBinderIpc pointer is not NULL */ return gbinder_ipc_priv_get_remote_object(self->priv, GBINDER_SERVICEMANAGER_HANDLE, REMOTE_REGISTRY_CAN_CREATE, TRUE); } GBINDER_INLINE_FUNC GBinderIpcPriv* gbinder_ipc_priv_from_object_registry( GBinderObjectRegistry* reg) { return G_CAST(reg, GBinderIpcPriv, object_registry); } GBINDER_INLINE_FUNC GBinderIpc* gbinder_ipc_from_object_registry( GBinderObjectRegistry* reg) { return gbinder_ipc_priv_from_object_registry(reg)->self; } static void gbinder_ipc_object_registry_ref( GBinderObjectRegistry* reg) { gbinder_ipc_ref(gbinder_ipc_from_object_registry(reg)); } static void gbinder_ipc_object_registry_unref( GBinderObjectRegistry* reg) { gbinder_ipc_unref(gbinder_ipc_from_object_registry(reg)); } static GBinderLocalObject* gbinder_ipc_object_registry_get_local( GBinderObjectRegistry* reg, void* pointer) { return gbinder_ipc_priv_get_local_object (gbinder_ipc_priv_from_object_registry(reg), pointer); } static GBinderRemoteObject* gbinder_ipc_object_registry_get_remote( GBinderObjectRegistry* reg, guint32 handle, REMOTE_REGISTRY_CREATE create) { return gbinder_ipc_priv_get_remote_object (gbinder_ipc_priv_from_object_registry(reg), handle, create, FALSE); } /*==========================================================================* * Implementation *==========================================================================*/ static gulong gbinder_ipc_tx_new_id() { static gint gbinder_ipc_next_id = 1; gulong id = (guint)g_atomic_int_add(&gbinder_ipc_next_id, 1); if (!id) id = (guint)g_atomic_int_add(&gbinder_ipc_next_id, 1); return id; } static gulong gbinder_ipc_tx_get_id( GBinderIpc* self) { GBinderIpcPriv* priv = self->priv; gulong id = gbinder_ipc_tx_new_id(); while (g_hash_table_contains(priv->tx_table, GINT_TO_POINTER(id))) { id = gbinder_ipc_tx_new_id(); } return id; } static void gbinder_ipc_tx_free( gpointer data) { GBinderIpcTxPriv* tx = data; GBinderIpcTx* pub = &tx->pub; GBinderIpc* self = pub->ipc; GBinderIpcPriv* priv = self->priv; gbinder_idle_callback_unref(tx->completion); g_hash_table_remove(priv->tx_table, GINT_TO_POINTER(pub->id)); tx->fn_free(tx); /* This may actually deallocate GBinderIpc object: */ gbinder_ipc_unref(self); } static void gbinder_ipc_tx_done( gpointer data) { GBinderIpcTxPriv* tx = data; GBinderIpcTx* pub = &tx->pub; if (!pub->cancelled) { tx->fn_done(tx); } } static void gbinder_ipc_tx_pub_init( GBinderIpcTx* tx, GBinderIpc* self, gulong id, void* user_data) { tx->id = id; tx->ipc = gbinder_ipc_ref(self); tx->user_data = user_data; } static void gbinder_ipc_tx_priv_init( GBinderIpcTxPriv* priv, GBinderIpc* self, gulong id, void* user_data, GBinderIpcTxPrivFunc fn_exec, GBinderIpcTxPrivFunc fn_done, GBinderIpcTxPrivFunc fn_free) { gbinder_ipc_tx_pub_init(&priv->pub, self, id, user_data); priv->fn_exec = fn_exec; priv->fn_done = fn_done; priv->fn_free = fn_free; priv->completion = gbinder_idle_callback_new(gbinder_ipc_tx_done, priv, gbinder_ipc_tx_free); } static inline GBinderIpcTxInternal* gbinder_ipc_tx_internal_cast( GBinderIpcTxPriv* priv) { return G_CAST(priv, GBinderIpcTxInternal, tx); } static void gbinder_ipc_tx_internal_free( GBinderIpcTxPriv* priv) { GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv); GBinderIpcTx* pub = &priv->pub; gbinder_local_request_unref(tx->req); gbinder_remote_reply_unref(tx->reply); if (tx->fn_destroy) { tx->fn_destroy(pub->user_data); } g_slice_free(GBinderIpcTxInternal, tx); } static void gbinder_ipc_tx_internal_done( GBinderIpcTxPriv* priv) { GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv); GBinderIpcTx* pub = &priv->pub; if (tx->fn_reply) { tx->fn_reply(pub->ipc, tx->reply, tx->status, pub->user_data); } } static void gbinder_ipc_tx_internal_exec( GBinderIpcTxPriv* priv) { GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv); GBinderIpc* ipc = priv->pub.ipc; if (tx->flags & GBINDER_TX_FLAG_ONEWAY) { tx->status = gbinder_ipc_transact_sync_oneway_worker(ipc, tx->handle, tx->code, tx->req); } else { tx->reply = gbinder_ipc_transact_sync_reply_worker(ipc, tx->handle, tx->code, tx->req, &tx->status); } } static GBinderIpcTxPriv* gbinder_ipc_tx_internal_new( GBinderIpc* self, gulong id, guint32 handle, guint32 code, guint32 flags, GBinderLocalRequest* req, GBinderIpcReplyFunc reply, GDestroyNotify destroy, void* user_data) { GBinderIpcTxInternal* tx = g_slice_new0(GBinderIpcTxInternal); GBinderIpcTxPriv* priv = &tx->tx; gbinder_ipc_tx_priv_init(priv, self, id, user_data, gbinder_ipc_tx_internal_exec, gbinder_ipc_tx_internal_done, gbinder_ipc_tx_internal_free); tx->code = code; tx->flags = flags; tx->handle = handle; tx->req = gbinder_local_request_ref(req); tx->fn_reply = reply; tx->fn_destroy = destroy; return priv; } static inline GBinderIpcTxCustom* gbinder_ipc_tx_custom_cast( GBinderIpcTxPriv* priv) { return G_CAST(priv, GBinderIpcTxCustom, tx); } static void gbinder_ipc_tx_custom_free( GBinderIpcTxPriv* priv) { GBinderIpcTxCustom* tx = gbinder_ipc_tx_custom_cast(priv); if (tx->fn_custom_destroy) { tx->fn_custom_destroy(priv->pub.user_data); } g_slice_free(GBinderIpcTxCustom, tx); } static void gbinder_ipc_tx_custom_done( GBinderIpcTxPriv* priv) { GBinderIpcTxCustom* tx = gbinder_ipc_tx_custom_cast(priv); if (tx->fn_custom_done) { tx->fn_custom_done(&priv->pub); } } static void gbinder_ipc_tx_custom_exec( GBinderIpcTxPriv* priv) { GBinderIpcTxCustom* tx = gbinder_ipc_tx_custom_cast(priv); if (tx->fn_custom_exec) { tx->fn_custom_exec(&priv->pub); } } static GBinderIpcTxPriv* gbinder_ipc_tx_custom_new( GBinderIpc* self, gulong id, GBinderIpcTxFunc exec, GBinderIpcTxFunc done, GDestroyNotify destroy, void* user_data) { GBinderIpcTxCustom* tx = g_slice_new0(GBinderIpcTxCustom); GBinderIpcTxPriv* priv = &tx->tx; gbinder_ipc_tx_priv_init(priv, self, id, user_data, gbinder_ipc_tx_custom_exec, gbinder_ipc_tx_custom_done, gbinder_ipc_tx_custom_free); tx->fn_custom_exec = exec; tx->fn_custom_done = done; tx->fn_custom_destroy = destroy; return priv; } /* Invoked on a thread from tx_pool */ static void gbinder_ipc_tx_proc( gpointer data, gpointer object) { GBinderIpcTxPriv* tx = data; if (!tx->pub.cancelled) { tx->fn_exec(tx); } else { GVERBOSE_("not executing transaction %lu (cancelled)", tx->pub.id); } /* The result is handled by the main thread */ gbinder_idle_callback_schedule(tx->completion); } /*==========================================================================* * GBinderIpcSyncApi for worker threads *==========================================================================*/ static GBinderRemoteReply* gbinder_ipc_transact_sync_reply_worker( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req, int* status) { /* Must be invoked on worker thread */ if (G_LIKELY(self)) { static const GBinderHandlerFunctions handler_fn = { .can_loop = NULL, .transact = gbinder_ipc_tx_handler_transact }; GBinderHandler handler = { &handler_fn }; GBinderIpcPriv* priv = self->priv; GBinderObjectRegistry* reg = &priv->object_registry; GBinderRemoteReply* reply = gbinder_remote_reply_new(reg); int ret = gbinder_driver_transact(self->driver, reg, &handler, handle, code, req, reply); if (status) *status = ret; if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) { return reply; } else { gbinder_remote_reply_unref(reply); } } else { if (status) *status = (-EINVAL); } return NULL; } static int gbinder_ipc_transact_sync_oneway_worker( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req) { /* Must be invoked on worker thread */ if (G_LIKELY(self)) { static const GBinderHandlerFunctions handler_fn = { .can_loop = NULL, .transact = gbinder_ipc_tx_handler_transact }; GBinderHandler handler = { &handler_fn }; GBinderIpcPriv* priv = self->priv; return gbinder_driver_transact(self->driver, &priv->object_registry, &handler, handle, code, req, NULL); } else { return (-EINVAL); } } const GBinderIpcSyncApi gbinder_ipc_sync_worker = { .sync_reply = gbinder_ipc_transact_sync_reply_worker, .sync_oneway = gbinder_ipc_transact_sync_oneway_worker }; /*==========================================================================* * GBinderIpcSyncApi for the main thread *==========================================================================*/ GBinderRemoteReply* gbinder_ipc_transact_sync_reply_main( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req, int* status) { if (G_LIKELY(self)) { GBinderIpcPriv* priv = self->priv; GBinderObjectRegistry* reg = &priv->object_registry; GBinderRemoteReply* reply = gbinder_remote_reply_new(reg); int ret = gbinder_driver_transact(self->driver, reg, NULL, handle, code, req, reply); if (status) *status = ret; if (ret == GBINDER_STATUS_OK || !gbinder_remote_reply_is_empty(reply)) { return reply; } else { gbinder_remote_reply_unref(reply); } } else { if (status) *status = (-EINVAL); } return NULL; } int gbinder_ipc_transact_sync_oneway_main( GBinderIpc* self, guint32 handle, guint32 code, GBinderLocalRequest* req) { if (G_LIKELY(self)) { GBinderIpcPriv* priv = self->priv; return gbinder_driver_transact(self->driver, &priv->object_registry, NULL, handle, code, req, NULL); } else { return (-EINVAL); } } const GBinderIpcSyncApi gbinder_ipc_sync_main = { .sync_reply = gbinder_ipc_transact_sync_reply_main, .sync_oneway = gbinder_ipc_transact_sync_oneway_main }; /*==========================================================================* * Interface *==========================================================================*/ GBinderIpc* gbinder_ipc_new( const char* dev, const char* protocol_name) { GBinderIpc* self = NULL; char* key; const GBinderRpcProtocol* protocol = (protocol_name ? gbinder_rpc_protocol_by_name(protocol_name) : NULL); if (!dev || !dev[0]) dev = GBINDER_DEFAULT_BINDER; if (!protocol) protocol = gbinder_rpc_protocol_for_device(dev); key = gbinder_ipc_make_key(dev, protocol->name); /* Lock */ pthread_mutex_lock(&gbinder_ipc_mutex); if (gbinder_ipc_table) { self = g_hash_table_lookup(gbinder_ipc_table, key); } if (self) { g_free(key); gbinder_ipc_ref(self); } else { GBinderDriver* driver = gbinder_driver_new(dev, protocol); if (driver) { GBinderIpcPriv* priv; self = g_object_new(THIS_TYPE, NULL); priv = self->priv; self->driver = driver; self->dev = priv->dev = g_strdup(dev); priv->key = key; self->priv->object_registry.io = gbinder_driver_io(driver); /* gbinder_ipc_dispose will remove iself from the table */ if (!gbinder_ipc_table) { gbinder_ipc_table = g_hash_table_new(g_str_hash, g_str_equal); } g_hash_table_replace(gbinder_ipc_table, priv->key, self); /* With "/dev/" prefix, it may be too long to be a thread name */ priv->name = self->dev + (g_str_has_prefix(priv->dev, "/dev/") ? 5 : 0); } else { g_free(key); } } pthread_mutex_unlock(&gbinder_ipc_mutex); /* Unlock */ return self; } GBinderIpc* gbinder_ipc_ref( GBinderIpc* self) { if (G_LIKELY(self)) { g_object_ref(THIS(self)); return self; } else { return NULL; } } void gbinder_ipc_unref( GBinderIpc* self) { if (G_LIKELY(self)) { g_object_unref(THIS(self)); } } const char* gbinder_ipc_name( GBinderIpc* self) { return G_LIKELY(self) ? self->priv->name : NULL; } GBinderObjectRegistry* gbinder_ipc_object_registry( GBinderIpc* self) { return G_LIKELY(self) ? &self->priv->object_registry : NULL; } const GBinderIo* gbinder_ipc_io( GBinderIpc* self) { return G_LIKELY(self) ? gbinder_driver_io(self->driver) : NULL; } const GBinderRpcProtocol* gbinder_ipc_protocol( GBinderIpc* self) { return G_LIKELY(self) ? gbinder_driver_protocol(self->driver) : NULL; } int gbinder_ipc_ping_sync( GBinderIpc* ipc, guint32 handle, const GBinderIpcSyncApi* api) { GBinderDriver* driver = ipc->driver; GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver); guint32 code = gbinder_driver_protocol(driver)->ping_tx; int ret; gbinder_remote_reply_unref(api->sync_reply(ipc, handle, code, req, &ret)); gbinder_local_request_unref(req); return ret; } gulong gbinder_ipc_transact( GBinderIpc* self, guint32 handle, guint32 code, guint32 flags, GBinderLocalRequest* req, GBinderIpcReplyFunc reply, GDestroyNotify destroy, void* user_data) { if (G_LIKELY(self)) { GBinderIpcPriv* priv = self->priv; GBinderIpcTxPriv* tx = gbinder_ipc_tx_internal_new(self, gbinder_ipc_tx_get_id(self), handle, code, flags, req, reply, destroy, user_data); const gulong id = tx->pub.id; g_hash_table_insert(priv->tx_table, GINT_TO_POINTER(id), tx); g_thread_pool_push(priv->tx_pool, tx, NULL); return id; } else { return 0; } } gulong gbinder_ipc_transact_custom( GBinderIpc* self, GBinderIpcTxFunc exec, GBinderIpcTxFunc done, GDestroyNotify destroy, void* user_data) { if (G_LIKELY(self)) { GBinderIpcPriv* priv = self->priv; GBinderIpcTxPriv* tx = gbinder_ipc_tx_custom_new(self, gbinder_ipc_tx_get_id(self), exec, done, destroy, user_data); const gulong id = tx->pub.id; g_hash_table_insert(priv->tx_table, GINT_TO_POINTER(id), tx); g_thread_pool_push(priv->tx_pool, tx, NULL); return id; } else { return 0; } } void gbinder_ipc_cancel( GBinderIpc* self, gulong id) { if (G_LIKELY(self) && G_LIKELY(id)) { gconstpointer key = GINT_TO_POINTER(id); GBinderIpcPriv* priv = self->priv; GBinderIpcTx* tx = g_hash_table_lookup(priv->tx_table, key); if (tx) { tx->cancelled = TRUE; GVERBOSE_("%lu", id); } else { GWARN("Invalid transaction id %lu", id); } } } gboolean gbinder_ipc_set_max_threads( GBinderIpc* self, gint max) { return g_thread_pool_set_max_threads(self->priv->tx_pool, max, NULL); } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_ipc_init( GBinderIpc* self) { static const GBinderObjectRegistryFunctions object_registry_functions = { .ref = gbinder_ipc_object_registry_ref, .unref = gbinder_ipc_object_registry_unref, .get_local = gbinder_ipc_object_registry_get_local, .get_remote = gbinder_ipc_object_registry_get_remote }; GBinderIpcPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self, THIS_TYPE, GBinderIpcPriv); g_mutex_init(&priv->looper_mutex); g_mutex_init(&priv->local_objects_mutex); g_mutex_init(&priv->remote_objects_mutex); priv->tx_table = g_hash_table_new(g_direct_hash, g_direct_equal); priv->tx_pool = g_thread_pool_new(gbinder_ipc_tx_proc, self, GBINDER_IPC_MAX_TX_THREADS, FALSE, NULL); priv->object_registry.f = &object_registry_functions; priv->self = self; self->priv = priv; } static void gbinder_ipc_stop_loopers( GBinderIpc* self) { GBinderIpcPriv* priv = self->priv; GBinderIpcLooper* loopers = NULL; do { GBinderIpcLooper* tmp; /* Lock */ g_mutex_lock(&priv->looper_mutex); loopers = gbinder_ipc_looper_stop_all(gbinder_ipc_looper_stop_all(NULL, priv->primary_loopers), priv->blocked_loopers); priv->blocked_loopers = NULL; priv->primary_loopers = NULL; g_mutex_unlock(&priv->looper_mutex); /* Unlock */ tmp = loopers; while (tmp) { GBinderIpcLooper* looper = tmp; tmp = looper->next; looper->next = NULL; gbinder_ipc_looper_join(looper); gbinder_ipc_looper_unref(looper); } } while (loopers); } static void gbinder_ipc_dispose( GObject* object) { GBinderIpc* self = THIS(object); GVERBOSE_("%s", self->dev); /* Lock */ pthread_mutex_lock(&gbinder_ipc_mutex); /* * gbinder_ipc_dispose() can be invoked more than once (typically * at shutdown) and gbinder_ipc_table here may actually happen to * be NULL, hence the check. */ if (gbinder_ipc_table) { GBinderIpcPriv* priv = self->priv; GASSERT(g_hash_table_lookup(gbinder_ipc_table, priv->key) == self); g_hash_table_remove(gbinder_ipc_table, priv->key); if (g_hash_table_size(gbinder_ipc_table) == 0) { g_hash_table_unref(gbinder_ipc_table); gbinder_ipc_table = NULL; } } pthread_mutex_unlock(&gbinder_ipc_mutex); /* Unlock */ gbinder_ipc_stop_loopers(self); G_OBJECT_CLASS(PARENT_CLASS)->dispose(object); } static void gbinder_ipc_finalize( GObject* object) { GBinderIpc* self = THIS(object); GBinderIpcPriv* priv = self->priv; GASSERT(!priv->local_objects); GASSERT(!priv->remote_objects); g_mutex_clear(&priv->looper_mutex); g_mutex_clear(&priv->local_objects_mutex); g_mutex_clear(&priv->remote_objects_mutex); if (priv->tx_pool) { g_thread_pool_free(priv->tx_pool, FALSE, TRUE); } GASSERT(!g_hash_table_size(priv->tx_table)); g_hash_table_unref(priv->tx_table); gbinder_driver_unref(self->driver); g_free(priv->dev); g_free(priv->key); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_ipc_class_init( GBinderIpcClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); g_type_class_add_private(klass, sizeof(GBinderIpcPriv)); object_class->dispose = gbinder_ipc_dispose; object_class->finalize = gbinder_ipc_finalize; } /* Runs at exit */ void gbinder_ipc_exit() { GHashTableIter it; gpointer key, value; GSList* ipcs = NULL; GSList* i; /* Lock */ pthread_mutex_lock(&gbinder_ipc_mutex); if (gbinder_ipc_table) { g_hash_table_iter_init(&it, gbinder_ipc_table); while (g_hash_table_iter_next(&it, NULL, &value)) { ipcs = g_slist_append(ipcs, gbinder_ipc_ref(value)); } } pthread_mutex_unlock(&gbinder_ipc_mutex); /* Unlock */ for (i = ipcs; i; i = i->next) { GBinderIpc* ipc = THIS(i->data); GBinderIpcPriv* priv = ipc->priv; GSList* local_objs = NULL; GSList* tx_keys = NULL; GSList* k; GSList* l; /* Terminate looper threads */ GVERBOSE_("%s", ipc->dev); gbinder_ipc_stop_loopers(ipc); /* Make sure pooled transaction complete too */ if (priv->tx_pool) { GThreadPool* pool = priv->tx_pool; priv->tx_pool = NULL; g_thread_pool_free(pool, FALSE, TRUE); } /* * Since this function is supposed to be invoked on the main thread, * there's no need to synchronize access to priv->tx_table. In any * case, this must be the last thread associated with this object. */ g_hash_table_iter_init(&it, priv->tx_table); while (g_hash_table_iter_next(&it, &key, NULL)) { tx_keys = g_slist_append(tx_keys, key); } for (k = tx_keys; k; k = k->next) { GBinderIpcTxPriv* tx = g_hash_table_lookup(priv->tx_table, k->data); GVERBOSE_("tx %lu", tx->pub.id); gbinder_idle_callback_cancel(tx->completion); } /* The above loop must destroy all uncompleted transactions */ GASSERT(!g_hash_table_size(priv->tx_table)); g_slist_free(tx_keys); /* Lock */ g_mutex_lock(&priv->local_objects_mutex); if (priv->local_objects) { g_hash_table_iter_init(&it, priv->local_objects); while (g_hash_table_iter_next(&it, NULL, &value)) { local_objs = g_slist_append(local_objs, gbinder_local_object_ref(value)); } } g_mutex_unlock(&priv->local_objects_mutex); /* Unlock */ /* Drop remote references */ for (l = local_objs; l; l = l->next) { GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(l->data); GVERBOSE_("%p has %d strong ref(s)", obj, obj->strong_refs); while (obj->strong_refs > 0) { obj->strong_refs--; gbinder_local_object_unref(obj); } } g_slist_free_full(local_objs, g_object_unref); } g_slist_free_full(ipcs, g_object_unref); gbinder_eventloop_set(NULL); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_ipc.h000066400000000000000000000135061526127616700171320ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_IPC_H #define GBINDER_IPC_H #include "gbinder_types_p.h" #include typedef struct gbinder_ipc_priv GBinderIpcPriv; struct gbinder_ipc { GObject object; GBinderIpcPriv* priv; GBinderDriver* driver; const char* dev; }; typedef struct gbinder_ipc_tx GBinderIpcTx; typedef void (*GBinderIpcTxFunc)( const GBinderIpcTx* tx); struct gbinder_ipc_tx { gulong id; gboolean cancelled; GBinderIpc* ipc; void* user_data; }; typedef gboolean (*GBinderIpcLocalObjectCheckFunc)( GBinderLocalObject* obj, void* user_data); typedef void (*GBinderIpcReplyFunc)( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data); typedef GBinderRemoteReply* (*GBinderIpcSyncReplyFunc)( GBinderIpc* ipc, guint32 handle, guint32 code, GBinderLocalRequest* req, int* status); typedef int (*GBinderIpcSyncOnewayFunc)( GBinderIpc* ipc, guint32 handle, guint32 code, GBinderLocalRequest* req); struct gbinder_ipc_sync_api { GBinderIpcSyncReplyFunc sync_reply; GBinderIpcSyncOnewayFunc sync_oneway; }; extern const GBinderIpcSyncApi gbinder_ipc_sync_main GBINDER_INTERNAL; extern const GBinderIpcSyncApi gbinder_ipc_sync_worker GBINDER_INTERNAL; GBinderIpc* gbinder_ipc_new( const char* dev, const char* protocol) GBINDER_INTERNAL; GBinderIpc* gbinder_ipc_ref( GBinderIpc* ipc) GBINDER_INTERNAL; void gbinder_ipc_unref( GBinderIpc* ipc) GBINDER_INTERNAL; const char* gbinder_ipc_name( GBinderIpc* ipc) GBINDER_INTERNAL; void gbinder_ipc_looper_check( GBinderIpc* ipc) GBINDER_INTERNAL; GBinderObjectRegistry* gbinder_ipc_object_registry( GBinderIpc* ipc) GBINDER_INTERNAL; const GBinderIo* gbinder_ipc_io( GBinderIpc* ipc) GBINDER_INTERNAL; const GBinderRpcProtocol* gbinder_ipc_protocol( GBinderIpc* ipc) GBINDER_INTERNAL; GBinderLocalObject* gbinder_ipc_find_local_object( GBinderIpc* ipc, GBinderIpcLocalObjectCheckFunc func, void* user_data) GBINDER_INTERNAL G_GNUC_WARN_UNUSED_RESULT; void gbinder_ipc_register_local_object( GBinderIpc* ipc, GBinderLocalObject* obj) GBINDER_INTERNAL; GBinderRemoteObject* gbinder_ipc_get_service_manager( GBinderIpc* ipc) GBINDER_INTERNAL G_GNUC_WARN_UNUSED_RESULT; void gbinder_ipc_invalidate_remote_handle( GBinderIpc* ipc, guint32 handle) GBINDER_INTERNAL; int gbinder_ipc_ping_sync( GBinderIpc* ipc, guint32 handle, const GBinderIpcSyncApi* api) GBINDER_INTERNAL; GBinderRemoteReply* gbinder_ipc_transact_sync_reply_main( GBinderIpc* ipc, guint32 handle, guint32 code, GBinderLocalRequest* req, int* status) GBINDER_INTERNAL; int gbinder_ipc_transact_sync_oneway_main( GBinderIpc* ipc, guint32 handle, guint32 code, GBinderLocalRequest* req) GBINDER_INTERNAL; gulong gbinder_ipc_transact( GBinderIpc* ipc, guint32 handle, guint32 code, guint32 flags, /* GBINDER_TX_FLAG_xxx */ GBinderLocalRequest* req, GBinderIpcReplyFunc func, GDestroyNotify destroy, void* user_data) GBINDER_INTERNAL; gulong gbinder_ipc_transact_custom( GBinderIpc* ipc, GBinderIpcTxFunc exec, GBinderIpcTxFunc done, GDestroyNotify destroy, void* user_data) GBINDER_INTERNAL; void gbinder_ipc_cancel( GBinderIpc* ipc, gulong id) GBINDER_INTERNAL; /* Internal for GBinderLocalObject */ void gbinder_ipc_local_object_disposed( GBinderIpc* ipc, GBinderLocalObject* obj) GBINDER_INTERNAL; void gbinder_ipc_invalidate_local_object( GBinderIpc* ipc, GBinderLocalObject* obj) GBINDER_INTERNAL; /* Internal for GBinderRemoteObject */ void gbinder_ipc_remote_object_disposed( GBinderIpc* ipc, GBinderRemoteObject* obj) GBINDER_INTERNAL; /* Needed by unit tests */ gboolean gbinder_ipc_set_max_threads( GBinderIpc* ipc, gint max_threads) GBINDER_INTERNAL; /* Declared for unit tests */ void gbinder_ipc_exit( void) GBINDER_INTERNAL GBINDER_DESTRUCTOR; #endif /* GBINDER_IPC_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_object.c000066400000000000000000000464711526127616700210010ustar00rootroot00000000000000/* * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_buffer_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_remote_request.h" #include "gbinder_eventloop_p.h" #include "gbinder_writer.h" #include "gbinder_log.h" #include #include #include struct gbinder_local_object_priv { char** ifaces; GBinderLocalTransactFunc txproc; void* user_data; }; typedef struct gbinder_local_object_acquire_data { GBinderLocalObject* object; GBinderBufferContentsList* bufs; } GBinderLocalObjectAcquireData; G_DEFINE_TYPE(GBinderLocalObject, gbinder_local_object, G_TYPE_OBJECT) #define PARENT_CLASS gbinder_local_object_parent_class #define GBINDER_LOCAL_OBJECT_GET_CLASS(obj) \ G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_LOCAL_OBJECT, \ GBinderLocalObjectClass) typedef GBinderLocalReply* (*GBinderLocalObjectTxHandler)( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status); enum gbinder_local_object_signal { SIGNAL_WEAK_REFS_CHANGED, SIGNAL_STRONG_REFS_CHANGED, SIGNAL_COUNT }; #define SIGNAL_WEAK_REFS_CHANGED_NAME "weak_refs_changed" #define SIGNAL_STRONG_REFS_CHANGED_NAME "strong_refs_changed" static guint gbinder_local_object_signals[SIGNAL_COUNT] = { 0 }; static const char hidl_base_interface[] = "android.hidl.base@1.0::IBase"; /*==========================================================================* * Implementation *==========================================================================*/ static GBinderLocalReply* gbinder_local_object_create_reply( GBinderLocalObject* self) { return gbinder_local_reply_new(gbinder_local_object_io(self), gbinder_local_object_protocol(self)); } static GBINDER_LOCAL_TRANSACTION_SUPPORT gbinder_local_object_default_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { switch (code) { case GBINDER_PING_TRANSACTION: case GBINDER_INTERFACE_TRANSACTION: return GBINDER_LOCAL_TRANSACTION_LOOPER; case HIDL_PING_TRANSACTION: case HIDL_GET_DESCRIPTOR_TRANSACTION: case HIDL_DESCRIPTOR_CHAIN_TRANSACTION: if (!g_strcmp0(iface, hidl_base_interface)) { return GBINDER_LOCAL_TRANSACTION_LOOPER; } /* no break */ default: return self->priv->txproc ? GBINDER_LOCAL_TRANSACTION_SUPPORTED : GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED; } } static GBinderLocalReply* gbinder_local_object_default_handle_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status) { GBinderLocalObjectPriv* priv = self->priv; if (priv->txproc) { return priv->txproc(self, req, code, flags, status, priv->user_data); } else { if (status) *status = (-EBADMSG); return NULL; } } static GBinderLocalReply* gbinder_local_object_ping_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status) { GBinderLocalReply* reply = gbinder_local_object_create_reply(self); GVERBOSE(" PING_TRANSACTION"); gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK); *status = GBINDER_STATUS_OK; return reply; } static GBinderLocalReply* gbinder_local_object_interface_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status) { GBinderLocalObjectPriv* priv = self->priv; GBinderLocalReply* reply = gbinder_local_object_create_reply(self); GVERBOSE(" INTERFACE_TRANSACTION"); gbinder_local_reply_append_string16(reply, priv->ifaces[0]); *status = GBINDER_STATUS_OK; return reply; } static GBinderLocalReply* gbinder_local_object_hidl_ping_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status) { /*android.hidl.base@1.0::IBase interfaceDescriptor() */ GBinderLocalReply* reply = gbinder_local_object_create_reply(self); GVERBOSE(" HIDL_PING_TRANSACTION \"%s\"", gbinder_remote_request_interface(req)); gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK); *status = GBINDER_STATUS_OK; return reply; } static GBinderLocalReply* gbinder_local_object_hidl_get_descriptor_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status) { /*android.hidl.base@1.0::IBase interfaceDescriptor() */ GBinderLocalObjectPriv* priv = self->priv; GBinderLocalReply* reply = gbinder_local_object_create_reply(self); GBinderWriter writer; GVERBOSE(" HIDL_GET_DESCRIPTOR_TRANSACTION \"%s\"", gbinder_remote_request_interface(req)); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_hidl_string(&writer, priv->ifaces[0]); *status = GBINDER_STATUS_OK; return reply; } static GBinderLocalReply* gbinder_local_object_hidl_descriptor_chain_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, int* status) { /*android.hidl.base@1.0::IBase interfaceChain() */ GBinderLocalReply* reply = gbinder_local_object_create_reply(self); GBinderWriter writer; GVERBOSE(" HIDL_DESCRIPTOR_CHAIN_TRANSACTION \"%s\"", gbinder_remote_request_interface(req)); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_hidl_string_vec(&writer, (const char**) self->ifaces, -1); *status = GBINDER_STATUS_OK; return reply; } static GBinderLocalReply* gbinder_local_object_default_handle_looper_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status) { GBinderLocalObjectTxHandler handler = NULL; switch (code) { case GBINDER_PING_TRANSACTION: handler = gbinder_local_object_ping_transaction; break; case GBINDER_INTERFACE_TRANSACTION: handler = gbinder_local_object_interface_transaction; break; case HIDL_PING_TRANSACTION: handler = gbinder_local_object_hidl_ping_transaction; break; case HIDL_GET_DESCRIPTOR_TRANSACTION: handler = gbinder_local_object_hidl_get_descriptor_transaction; break; case HIDL_DESCRIPTOR_CHAIN_TRANSACTION: handler = gbinder_local_object_hidl_descriptor_chain_transaction; break; default: if (status) *status = (-EBADMSG); return NULL; } GASSERT(!(flags & GBINDER_TX_FLAG_ONEWAY)); return handler(self, req, status); } static void gbinder_local_object_default_drop( GBinderLocalObject* self) { GBinderLocalObjectPriv* priv = self->priv; /* Clear the transaction callback */ priv->txproc = NULL; priv->user_data = NULL; } static void gbinder_local_object_handle_later( GBinderLocalObject* self, GBinderEventLoopCallbackFunc function) { if (G_LIKELY(self)) { gbinder_idle_callback_invoke_later(function, gbinder_local_object_ref(self), g_object_unref); } } static void gbinder_local_object_increfs_proc( gpointer user_data) { GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data); self->weak_refs++; g_signal_emit(self, gbinder_local_object_signals [SIGNAL_WEAK_REFS_CHANGED], 0); } static void gbinder_local_object_decrefs_proc( gpointer user_data) { GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(user_data); GASSERT(self->weak_refs > 0); self->weak_refs--; g_signal_emit(self, gbinder_local_object_signals [SIGNAL_WEAK_REFS_CHANGED], 0); } static void gbinder_local_object_default_acquire( GBinderLocalObject* self) { self->strong_refs++; gbinder_local_object_ref(self); GVERBOSE_("%p => %d", self, self->strong_refs); g_signal_emit(self, gbinder_local_object_signals [SIGNAL_STRONG_REFS_CHANGED], 0); } static void gbinder_local_object_acquire_proc( gpointer user_data) { GBinderLocalObjectAcquireData* data = user_data; GBinderLocalObject* self = data->object; GBINDER_LOCAL_OBJECT_GET_CLASS(self)->acquire(self); } static void gbinder_local_object_acquire_done( gpointer user_data) { GBinderLocalObjectAcquireData* data = user_data; GBinderLocalObject* self = data->object; gbinder_driver_acquire_done(self->ipc->driver, self); gbinder_local_object_unref(self); gbinder_buffer_contents_list_free(data->bufs); return gutil_slice_free(data); } static void gbinder_local_object_default_release( GBinderLocalObject* self) { /* * Double-check the refs in case if gbinder_ipc_exit() cleared them * while this callback was being delivered to the main thread. */ if (self->strong_refs > 0) { self->strong_refs--; GVERBOSE_("%p => %d", self, self->strong_refs); g_signal_emit(self, gbinder_local_object_signals [SIGNAL_STRONG_REFS_CHANGED], 0); gbinder_local_object_unref(self); } } static void gbinder_local_object_release_proc( gpointer obj) { GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(obj); GBINDER_LOCAL_OBJECT_GET_CLASS(self)->release(self); } /*==========================================================================* * Interface *==========================================================================*/ GBinderLocalObject* gbinder_local_object_new( GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* user_data) /* Since 1.0.30 */ { return gbinder_local_object_new_with_type(GBINDER_TYPE_LOCAL_OBJECT, ipc, ifaces, txproc, user_data); } GBinderLocalObject* gbinder_local_object_new_with_type( GType type, GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* arg) { if (G_LIKELY(ipc)) { GBinderLocalObject* obj = g_object_new(type, NULL); gbinder_local_object_init_base(obj, ipc, ifaces, txproc, arg); gbinder_ipc_register_local_object(ipc, obj); return obj; } return NULL; } void gbinder_local_object_init_base( GBinderLocalObject* self, GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* user_data) { GBinderLocalObjectPriv* priv = self->priv; guint i = 0, n = gutil_strv_length((char**)ifaces); gboolean append_base_interface; if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) { append_base_interface = TRUE; n++; } else { append_base_interface = FALSE; } priv->ifaces = g_new(char*, n + 1); if (ifaces) { while (*ifaces) { priv->ifaces[i++] = g_strdup(*ifaces++); } } if (append_base_interface) { priv->ifaces[i++] = g_strdup(hidl_base_interface); } priv->ifaces[i] = NULL; self->ipc = gbinder_ipc_ref(ipc); self->ifaces = (const char**)priv->ifaces; self->stability = GBINDER_STABILITY_SYSTEM; priv->txproc = txproc; priv->user_data = user_data; } GBinderLocalObject* gbinder_local_object_ref( GBinderLocalObject* self) { if (G_LIKELY(self)) { g_object_ref(GBINDER_LOCAL_OBJECT(self)); return self; } else { return NULL; } } void gbinder_local_object_unref( GBinderLocalObject* self) { if (G_LIKELY(self)) { g_object_unref(GBINDER_LOCAL_OBJECT(self)); } } void gbinder_local_object_drop( GBinderLocalObject* self) { if (G_LIKELY(self)) { GBINDER_LOCAL_OBJECT_GET_CLASS(self)->drop(self); g_object_unref(GBINDER_LOCAL_OBJECT(self)); } } GBinderLocalReply* gbinder_local_object_new_reply( GBinderLocalObject* self) { if (G_LIKELY(self)) { return gbinder_local_object_create_reply(self); } return NULL; } gulong gbinder_local_object_add_weak_refs_changed_handler( GBinderLocalObject* self, GBinderLocalObjectFunc func, void* user_data) { return (G_LIKELY(self) && G_LIKELY(func)) ? g_signal_connect(self, SIGNAL_WEAK_REFS_CHANGED_NAME, G_CALLBACK(func), user_data) : 0; } gulong gbinder_local_object_add_strong_refs_changed_handler( GBinderLocalObject* self, GBinderLocalObjectFunc func, void* user_data) { return (G_LIKELY(self) && G_LIKELY(func)) ? g_signal_connect(self, SIGNAL_STRONG_REFS_CHANGED_NAME, G_CALLBACK(func), user_data) : 0; } void gbinder_local_object_remove_handler( GBinderLocalObject* self, gulong id) { if (G_LIKELY(self) && G_LIKELY(id)) { g_signal_handler_disconnect(self, id); } } GBINDER_LOCAL_TRANSACTION_SUPPORT gbinder_local_object_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { return G_LIKELY(self) ? GBINDER_LOCAL_OBJECT_GET_CLASS(self)->can_handle_transaction (self, iface, code) : GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED; } GBinderLocalReply* gbinder_local_object_handle_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status) { if (G_LIKELY(self)) { return GBINDER_LOCAL_OBJECT_GET_CLASS(self)->handle_transaction (self, req, code, flags, status); } else { if (status) *status = (-EBADMSG); return NULL; } } GBinderLocalReply* gbinder_local_object_handle_looper_transaction( GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status) { if (G_LIKELY(self)) { return GBINDER_LOCAL_OBJECT_GET_CLASS(self)->handle_looper_transaction (self, req, code, flags, status); } else { if (status) *status = -EBADMSG; return NULL; } } void gbinder_local_object_handle_increfs( GBinderLocalObject* self) { gbinder_local_object_handle_later(self, gbinder_local_object_increfs_proc); } void gbinder_local_object_handle_decrefs( GBinderLocalObject* self) { gbinder_local_object_handle_later(self, gbinder_local_object_decrefs_proc); } void gbinder_local_object_handle_acquire( GBinderLocalObject* self, GBinderBufferContentsList* bufs) { if (G_LIKELY(self)) { GBinderLocalObjectAcquireData* data = g_slice_new(GBinderLocalObjectAcquireData); /* * This is a bit complicated :) * GBinderProxyObject derived from GBinderLocalObject acquires a * reference to the remote object in addition to performing the * default GBinderLocalObject action later on the main thread. * We must ensure that remote object doesn't go away before we * acquire our reference to it. One of the references to that * remote object (possibly the last one) may be associated with * the transaction buffer contained in GBinderBufferContentsList. * We don't know exactly which one we need, so we keep all those * buffers alive until we are done with BR_ACQUIRE. */ data->object = gbinder_local_object_ref(self); data->bufs = gbinder_buffer_contents_list_dup(bufs); gbinder_idle_callback_invoke_later(gbinder_local_object_acquire_proc, data, gbinder_local_object_acquire_done); } } void gbinder_local_object_handle_release( GBinderLocalObject* self) { gbinder_local_object_handle_later(self, gbinder_local_object_release_proc); } void gbinder_local_object_set_stability( GBinderLocalObject* self, GBINDER_STABILITY_LEVEL stability) /* Since 1.1.40 */ { if (G_LIKELY(self)) { self->stability = stability; } } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_local_object_init( GBinderLocalObject* self) { GBinderLocalObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectPriv); self->priv = priv; } static void gbinder_local_object_dispose( GObject* object) { GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(object); gbinder_ipc_local_object_disposed(self->ipc, self); G_OBJECT_CLASS(PARENT_CLASS)->dispose(object); } static void gbinder_local_object_finalize( GObject* object) { GBinderLocalObject* self = GBINDER_LOCAL_OBJECT(object); GBinderLocalObjectPriv* priv = self->priv; GASSERT(!self->strong_refs); gbinder_ipc_invalidate_local_object(self->ipc, self); gbinder_ipc_unref(self->ipc); g_strfreev(priv->ifaces); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_local_object_class_init( GBinderLocalObjectClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); object_class->dispose = gbinder_local_object_dispose; object_class->finalize = gbinder_local_object_finalize; g_type_class_add_private(klass, sizeof(GBinderLocalObjectPriv)); klass->handle_transaction = gbinder_local_object_default_handle_transaction; klass->handle_looper_transaction = gbinder_local_object_default_handle_looper_transaction; klass->can_handle_transaction = gbinder_local_object_default_can_handle_transaction; klass->acquire = gbinder_local_object_default_acquire; klass->release = gbinder_local_object_default_release; klass->drop = gbinder_local_object_default_drop; gbinder_local_object_signals[SIGNAL_WEAK_REFS_CHANGED] = g_signal_new(SIGNAL_WEAK_REFS_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); gbinder_local_object_signals[SIGNAL_STRONG_REFS_CHANGED] = g_signal_new(SIGNAL_STRONG_REFS_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_object_p.h000066400000000000000000000136571526127616700213250ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_OBJECT_PRIVATE_H #define GBINDER_LOCAL_OBJECT_PRIVATE_H #include #include "gbinder_types_p.h" #include /* * Some of this stuff may become public if we decide to allow the clients * to derive their own classes from GBinderLocalObject. For now it's all * private. */ typedef void (*GBinderLocalObjectFunc)( GBinderLocalObject* obj, void* user_data); typedef struct gbinder_local_object_priv GBinderLocalObjectPriv; struct gbinder_local_object { GObject object; GBinderLocalObjectPriv* priv; GBinderIpc* ipc; const char* const* ifaces; gint weak_refs; gint strong_refs; GBINDER_STABILITY_LEVEL stability; }; typedef enum gbinder_local_transaction_support { GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED, GBINDER_LOCAL_TRANSACTION_SUPPORTED, /* On the main thread */ GBINDER_LOCAL_TRANSACTION_LOOPER /* On the looper thread */ } GBINDER_LOCAL_TRANSACTION_SUPPORT; typedef struct gbinder_local_object_class { GObjectClass parent; GBINDER_LOCAL_TRANSACTION_SUPPORT (*can_handle_transaction) (GBinderLocalObject* self, const char* iface, guint code); GBinderLocalReply* (*handle_transaction) (GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status); GBinderLocalReply* (*handle_looper_transaction) (GBinderLocalObject* self, GBinderRemoteRequest* req, guint code, guint flags, int* status); void (*acquire)(GBinderLocalObject* self); void (*release)(GBinderLocalObject* self); void (*drop)(GBinderLocalObject* self); /* Need to add some placeholders if this class becomes public */ } GBinderLocalObjectClass; GType gbinder_local_object_get_type(void) GBINDER_INTERNAL; #define GBINDER_TYPE_LOCAL_OBJECT (gbinder_local_object_get_type()) #define GBINDER_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObject)) #define GBINDER_LOCAL_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), \ GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectClass) #define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver)) #define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver)) #define gbinder_local_object_protocol(obj) \ (gbinder_driver_protocol((obj)->ipc->driver)) GBinderLocalObject* gbinder_local_object_new_with_type( GType type, GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* user_data) GBINDER_INTERNAL; void gbinder_local_object_init_base( GBinderLocalObject* self, GBinderIpc* ipc, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* user_data) GBINDER_INTERNAL; gulong gbinder_local_object_add_weak_refs_changed_handler( GBinderLocalObject* obj, GBinderLocalObjectFunc func, void* user_data) GBINDER_INTERNAL; gulong gbinder_local_object_add_strong_refs_changed_handler( GBinderLocalObject* obj, GBinderLocalObjectFunc func, void* user_data) GBINDER_INTERNAL; void gbinder_local_object_remove_handler( GBinderLocalObject* obj, gulong id) GBINDER_INTERNAL; GBINDER_LOCAL_TRANSACTION_SUPPORT gbinder_local_object_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) GBINDER_INTERNAL; GBinderLocalReply* gbinder_local_object_handle_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) GBINDER_INTERNAL; GBinderLocalReply* gbinder_local_object_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) GBINDER_INTERNAL; void gbinder_local_object_handle_increfs( GBinderLocalObject* obj) GBINDER_INTERNAL; void gbinder_local_object_handle_decrefs( GBinderLocalObject* obj) GBINDER_INTERNAL; void gbinder_local_object_handle_acquire( GBinderLocalObject* obj, GBinderBufferContentsList* bufs) GBINDER_INTERNAL; void gbinder_local_object_handle_release( GBinderLocalObject* obj) GBINDER_INTERNAL; #endif /* GBINDER_LOCAL_OBJECT_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_reply.c000066400000000000000000000201241526127616700206510ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd. * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_local_reply_p.h" #include "gbinder_output_data.h" #include "gbinder_writer_p.h" #include "gbinder_buffer_p.h" #include "gbinder_log.h" #include #include #include struct gbinder_local_reply { gint refcount; GBinderWriterData data; GBinderOutputData out; GBinderBufferContents* contents; }; GBINDER_INLINE_FUNC GBinderLocalReply* gbinder_local_reply_output_cast( GBinderOutputData* out) { return G_CAST(out, GBinderLocalReply, out); } static GUtilIntArray* gbinder_local_reply_output_offsets( GBinderOutputData* out) { return gbinder_local_reply_output_cast(out)->data.offsets; } static gsize gbinder_local_reply_output_buffers_size( GBinderOutputData* out) { return gbinder_local_reply_output_cast(out)->data.buffers_size; } GBinderLocalReply* gbinder_local_reply_new( const GBinderIo* io, const GBinderRpcProtocol* protocol) { GASSERT(io); GASSERT(protocol); if (io && protocol) { GBinderLocalReply* self = g_slice_new0(GBinderLocalReply); GBinderWriterData* data = &self->data; GBinderOutputData* out = &self->out; static const GBinderOutputDataFunctions local_reply_output_fn = { .offsets = gbinder_local_reply_output_offsets, .buffers_size = gbinder_local_reply_output_buffers_size }; g_atomic_int_set(&self->refcount, 1); data->io = io; data->protocol = protocol; out->bytes = data->bytes = g_byte_array_new(); out->f = &local_reply_output_fn; return self; } return NULL; } GBinderLocalReply* gbinder_local_reply_set_contents( GBinderLocalReply* self, GBinderBuffer* buffer, GBinderObjectConverter* convert) { if (self) { gbinder_writer_data_set_contents(&self->data, buffer, convert); gbinder_buffer_contents_unref(self->contents); self->contents = gbinder_buffer_contents_ref (gbinder_buffer_contents(buffer)); } return self; } static void gbinder_local_reply_free( GBinderLocalReply* self) { GBinderWriterData* data = &self->data; gutil_int_array_free(data->offsets, TRUE); g_byte_array_free(data->bytes, TRUE); gutil_cleanup_free(data->cleanup); gbinder_buffer_contents_unref(self->contents); gutil_slice_free(self); } GBinderLocalReply* gbinder_local_reply_ref( GBinderLocalReply* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return self; } void gbinder_local_reply_unref( GBinderLocalReply* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_local_reply_free(self); } } } GBinderOutputData* gbinder_local_reply_data( GBinderLocalReply* self) { return G_LIKELY(self) ? &self->out : NULL; } GBinderBufferContents* gbinder_local_reply_contents( GBinderLocalReply* self) { return G_LIKELY(self) ? self->contents : NULL; } void gbinder_local_reply_cleanup( GBinderLocalReply* self, GDestroyNotify destroy, gpointer pointer) { if (G_LIKELY(self)) { GBinderWriterData* data = &self->data; if (!data->cleanup) { data->cleanup = gutil_cleanup_new(); } gutil_cleanup_add(data->cleanup, destroy, pointer); } else if (destroy) { destroy(pointer); } } void gbinder_local_reply_init_writer( GBinderLocalReply* self, GBinderWriter* writer) { if (G_LIKELY(writer)) { gbinder_writer_init(writer, G_LIKELY(self) ? &self->data : NULL, 0); } } GBinderLocalReply* gbinder_local_reply_append_bool( GBinderLocalReply* self, gboolean value) { if (G_LIKELY(self)) { gbinder_writer_data_append_bool(&self->data, value); } return self; } GBinderLocalReply* gbinder_local_reply_append_int32( GBinderLocalReply* self, guint32 value) { if (G_LIKELY(self)) { gbinder_writer_data_append_int32(&self->data, value); } return self; } GBinderLocalReply* gbinder_local_reply_append_int64( GBinderLocalReply* self, guint64 value) { if (G_LIKELY(self)) { gbinder_writer_data_append_int64(&self->data, value); } return self; } GBinderLocalReply* gbinder_local_reply_append_float( GBinderLocalReply* self, gfloat value) { if (G_LIKELY(self)) { gbinder_writer_data_append_float(&self->data, value); } return self; } GBinderLocalReply* gbinder_local_reply_append_double( GBinderLocalReply* self, gdouble value) { if (G_LIKELY(self)) { gbinder_writer_data_append_double(&self->data, value); } return self; } GBinderLocalReply* gbinder_local_reply_append_string8( GBinderLocalReply* self, const char* str) { if (G_LIKELY(self)) { gbinder_writer_data_append_string8(&self->data, str); } return self; } GBinderLocalReply* gbinder_local_reply_append_string16( GBinderLocalReply* self, const char* utf8) { if (G_LIKELY(self)) { gbinder_writer_data_append_string16(&self->data, utf8); } return self; } GBinderLocalReply* gbinder_local_reply_append_hidl_string( GBinderLocalReply* self, const char* str) { if (G_LIKELY(self)) { gbinder_writer_data_append_hidl_string(&self->data, str); } return self; } GBinderLocalReply* gbinder_local_reply_append_hidl_string_vec( GBinderLocalReply* self, const char* strv[], gssize count) { if (G_LIKELY(self)) { gbinder_writer_data_append_hidl_string_vec(&self->data, strv, count); } return self; } GBinderLocalReply* gbinder_local_reply_append_local_object( GBinderLocalReply* self, GBinderLocalObject* obj) { if (G_LIKELY(self)) { gbinder_writer_data_append_local_object(&self->data, obj); } return self; } GBinderLocalReply* gbinder_local_reply_append_remote_object( GBinderLocalReply* self, GBinderRemoteObject* obj) { if (G_LIKELY(self)) { gbinder_writer_data_append_remote_object(&self->data, obj); } return self; } GBinderLocalReply* gbinder_local_reply_append_fd( GBinderLocalReply* self, int fd) /* Since 1.1.14 */ { if (G_LIKELY(self)) { gbinder_writer_data_append_fd(&self->data, fd); } return self; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_reply_p.h000066400000000000000000000047021526127616700212010ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_REPLY_PRIVATE_H #define GBINDER_LOCAL_REPLY_PRIVATE_H #include #include "gbinder_types_p.h" GBinderLocalReply* gbinder_local_reply_new( const GBinderIo* io, const GBinderRpcProtocol* protocol) GBINDER_INTERNAL; GBinderOutputData* gbinder_local_reply_data( GBinderLocalReply* reply) GBINDER_INTERNAL; GBinderBufferContents* gbinder_local_reply_contents( GBinderLocalReply* reply) GBINDER_INTERNAL; GBinderLocalReply* gbinder_local_reply_set_contents( GBinderLocalReply* reply, GBinderBuffer* buffer, GBinderObjectConverter* convert) GBINDER_INTERNAL; #endif /* GBINDER_LOCAL_REPLY_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_request.c000066400000000000000000000215341526127616700212140ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd. * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_local_request_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_output_data.h" #include "gbinder_writer_p.h" #include "gbinder_buffer_p.h" #include "gbinder_io.h" #include "gbinder_log.h" #include #include #include struct gbinder_local_request { gint refcount; GBinderWriterData data; GBinderOutputData out; }; GBINDER_INLINE_FUNC GBinderLocalRequest* gbinder_local_request_output_cast( GBinderOutputData* out) { return G_CAST(out, GBinderLocalRequest, out); } static GUtilIntArray* gbinder_local_request_output_offsets( GBinderOutputData* out) { return gbinder_local_request_output_cast(out)->data.offsets; } static gsize gbinder_local_request_output_buffers_size( GBinderOutputData* out) { return gbinder_local_request_output_cast(out)->data.buffers_size; } GBinderLocalRequest* gbinder_local_request_new( const GBinderIo* io, const GBinderRpcProtocol* protocol, GBytes* init) { GASSERT(io); GASSERT(protocol); if (io && protocol) { GBinderLocalRequest* self = g_slice_new0(GBinderLocalRequest); GBinderWriterData* writer = &self->data; GBinderOutputData* out = &self->out; static const GBinderOutputDataFunctions local_request_output_fn = { .offsets = gbinder_local_request_output_offsets, .buffers_size = gbinder_local_request_output_buffers_size }; g_atomic_int_set(&self->refcount, 1); writer->io = io; writer->protocol = protocol; if (init) { gsize size; gconstpointer data = g_bytes_get_data(init, &size); writer->bytes = g_byte_array_sized_new(size); g_byte_array_append(writer->bytes, data, size); } else { writer->bytes = g_byte_array_new(); } out->f = &local_request_output_fn; out->bytes = writer->bytes; return self; } return NULL; } GBinderLocalRequest* gbinder_local_request_new_iface( const GBinderIo* io, const GBinderRpcProtocol* protocol, const char* iface) { GBinderLocalRequest* self = gbinder_local_request_new(io, protocol, NULL); /* gbinder_local_request_new() fails if protocol is NULL */ if (self && G_LIKELY(iface)) { GBinderWriter writer; gbinder_local_request_init_writer(self, &writer); protocol->write_rpc_header(&writer, iface); } return self; } GBinderLocalRequest* gbinder_local_request_new_from_data( GBinderBuffer* buffer, GBinderObjectConverter* convert) { GBinderLocalRequest* self = gbinder_local_request_new (gbinder_buffer_io(buffer), gbinder_buffer_protocol(buffer), NULL); if (self) { gbinder_writer_data_append_contents(&self->data, buffer, 0, convert); } return self; } void gbinder_local_request_append_contents( GBinderLocalRequest* self, GBinderBuffer* buffer, gsize off, GBinderObjectConverter* convert) { if (self) { gbinder_writer_data_append_contents(&self->data, buffer, off, convert); } } static void gbinder_local_request_free( GBinderLocalRequest* self) { GBinderWriterData* data = &self->data; g_byte_array_free(data->bytes, TRUE); gutil_int_array_free(data->offsets, TRUE); gutil_cleanup_free(data->cleanup); g_slice_free(GBinderLocalRequest, self); } GBinderLocalRequest* gbinder_local_request_ref( GBinderLocalRequest* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return self; } void gbinder_local_request_unref( GBinderLocalRequest* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_local_request_free(self); } } } GBinderOutputData* gbinder_local_request_data( GBinderLocalRequest* self) { return G_LIKELY(self) ? &self->out : NULL; } void gbinder_local_request_cleanup( GBinderLocalRequest* self, GDestroyNotify destroy, gpointer pointer) { if (G_LIKELY(self)) { GBinderWriterData* data = &self->data; if (!data->cleanup) { data->cleanup = gutil_cleanup_new(); } gutil_cleanup_add(data->cleanup, destroy, pointer); } else if (destroy) { destroy(pointer); } } void gbinder_local_request_init_writer( GBinderLocalRequest* self, GBinderWriter* writer) { if (G_LIKELY(writer)) { gbinder_writer_init(writer, G_LIKELY(self) ? &self->data : NULL, 0); } } GBinderLocalRequest* gbinder_local_request_append_bool( GBinderLocalRequest* self, gboolean value) { if (G_LIKELY(self)) { gbinder_writer_data_append_bool(&self->data, value); } return self; } GBinderLocalRequest* gbinder_local_request_append_int32( GBinderLocalRequest* self, guint32 value) { if (G_LIKELY(self)) { gbinder_writer_data_append_int32(&self->data, value); } return self; } GBinderLocalRequest* gbinder_local_request_append_int64( GBinderLocalRequest* self, guint64 value) { if (G_LIKELY(self)) { gbinder_writer_data_append_int64(&self->data, value); } return self; } GBinderLocalRequest* gbinder_local_request_append_float( GBinderLocalRequest* self, gfloat value) { if (G_LIKELY(self)) { gbinder_writer_data_append_float(&self->data, value); } return self; } GBinderLocalRequest* gbinder_local_request_append_double( GBinderLocalRequest* self, gdouble value) { if (G_LIKELY(self)) { gbinder_writer_data_append_double(&self->data, value); } return self; } GBinderLocalRequest* gbinder_local_request_append_string8( GBinderLocalRequest* self, const char* str) { if (G_LIKELY(self)) { gbinder_writer_data_append_string8(&self->data, str); } return self; } GBinderLocalRequest* gbinder_local_request_append_string16( GBinderLocalRequest* self, const char* utf8) { if (G_LIKELY(self)) { gbinder_writer_data_append_string16(&self->data, utf8); } return self; } GBinderLocalRequest* gbinder_local_request_append_hidl_string( GBinderLocalRequest* self, const char* str) { if (G_LIKELY(self)) { gbinder_writer_data_append_hidl_string(&self->data, str); } return self; } GBinderLocalRequest* gbinder_local_request_append_hidl_string_vec( GBinderLocalRequest* self, const char* strv[], gssize count) { if (G_LIKELY(self)) { gbinder_writer_data_append_hidl_string_vec(&self->data, strv, count); } return self; } GBinderLocalRequest* gbinder_local_request_append_local_object( GBinderLocalRequest* self, GBinderLocalObject* obj) { if (G_LIKELY(self)) { gbinder_writer_data_append_local_object(&self->data, obj); } return self; } GBinderLocalRequest* gbinder_local_request_append_remote_object( GBinderLocalRequest* self, GBinderRemoteObject* obj) { if (G_LIKELY(self)) { gbinder_writer_data_append_remote_object(&self->data, obj); } return self; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_local_request_p.h000066400000000000000000000052671526127616700215450ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOCAL_REQUEST_PRIVATE_H #define GBINDER_LOCAL_REQUEST_PRIVATE_H #include #include "gbinder_types_p.h" GBinderLocalRequest* gbinder_local_request_new( const GBinderIo* io, const GBinderRpcProtocol* protocol, GBytes* init) GBINDER_INTERNAL; GBinderLocalRequest* gbinder_local_request_new_iface( const GBinderIo* io, const GBinderRpcProtocol* protocol, const char* iface) GBINDER_INTERNAL; GBinderLocalRequest* gbinder_local_request_new_from_data( GBinderBuffer* buffer, GBinderObjectConverter* convert) GBINDER_INTERNAL; GBinderOutputData* gbinder_local_request_data( GBinderLocalRequest* req) GBINDER_INTERNAL; void gbinder_local_request_append_contents( GBinderLocalRequest* req, GBinderBuffer* buffer, gsize offset, GBinderObjectConverter* convert) GBINDER_INTERNAL; #endif /* GBINDER_LOCAL_REQUEST_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_log.c000066400000000000000000000043451526127616700171340ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * * 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_log.h" #include #include /* Log module */ GLOG_MODULE_DEFINE("gbinder"); /* Initializes the default log level at startup */ void gbinder_log_init( void) { int level = GLOG_MODULE_NAME.level; if (gutil_parse_int(getenv("GBINDER_DEFAULT_LOG_LEVEL"), 0, &level) && level >= GLOG_LEVEL_INHERIT && level <= GLOG_LEVEL_VERBOSE) { GINFO("Log level %d", level); GLOG_MODULE_NAME.level = level; } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_log.h000066400000000000000000000040551526127616700171370ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_LOG_H #define GBINDER_LOG_H #include "gbinder_types_p.h" #define GLOG_MODULE_NAME GBINDER_LOG_MODULE #include /* Declared for unit tests */ __attribute__((constructor)) void gbinder_log_init( void) GBINDER_INTERNAL; #endif /* GBINDER_LOG_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_object_converter.h000066400000000000000000000056301526127616700217130ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * Copyright (C) 2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_OBJECT_CONVERTER_H #define GBINDER_OBJECT_CONVERTER_H #include "gbinder_types_p.h" typedef struct gbinder_object_converter_functions { GBinderLocalObject* (*handle_to_local)(GBinderObjectConverter*, guint32, const GBinderRpcProtocol*, const void*); GBinderRemoteObject* (*local_to_remote)(GBinderObjectConverter*, void*); } GBinderObjectConverterFunctions; struct gbinder_object_converter { const GBinderObjectConverterFunctions* f; const GBinderIo* io; const GBinderRpcProtocol* protocol; }; /* Inline wrappers */ GBINDER_INLINE_FUNC GBinderLocalObject* gbinder_object_converter_handle_to_local( GBinderObjectConverter* convert, guint32 handle, const GBinderRpcProtocol* protocol, const void* extra) { return convert ? convert->f->handle_to_local(convert, handle, protocol, extra) : NULL; } GBINDER_INLINE_FUNC GBinderRemoteObject* gbinder_object_converter_local_to_remote( GBinderObjectConverter* convert, void* pointer) { return (convert && convert->f->local_to_remote) ? convert->f->local_to_remote(convert, pointer) : NULL; } #endif /* GBINDER_OBJECT_CONVERTER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_object_registry.h000066400000000000000000000064271526127616700215610ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_OBJECT_REGISTRY_H #define GBINDER_OBJECT_REGISTRY_H #include "gbinder_types_p.h" typedef enum gbinder_remote_registry_create { REMOTE_REGISTRY_DONT_CREATE, REMOTE_REGISTRY_CAN_CREATE, REMOTE_REGISTRY_CAN_CREATE_AND_ACQUIRE } REMOTE_REGISTRY_CREATE; typedef struct gbinder_object_registry_functions { void (*ref)(GBinderObjectRegistry* reg); void (*unref)(GBinderObjectRegistry* reg); GBinderLocalObject* (*get_local)(GBinderObjectRegistry* reg, void* pointer); GBinderRemoteObject* (*get_remote)(GBinderObjectRegistry* reg, guint32 handle, REMOTE_REGISTRY_CREATE create); } GBinderObjectRegistryFunctions; struct gbinder_object_registry { const GBinderObjectRegistryFunctions* f; const GBinderIo* io; }; /* Inline wrappers */ GBINDER_INLINE_FUNC GBinderObjectRegistry* gbinder_object_registry_ref( GBinderObjectRegistry* reg) { if (reg) reg->f->ref(reg); return reg; } GBINDER_INLINE_FUNC void gbinder_object_registry_unref( GBinderObjectRegistry* reg) { if (reg) reg->f->unref(reg); } GBINDER_INLINE_FUNC GBinderLocalObject* gbinder_object_registry_get_local( GBinderObjectRegistry* reg, void* pointer) { return reg ? reg->f->get_local(reg, pointer) : NULL; } GBINDER_INLINE_FUNC GBinderRemoteObject* gbinder_object_registry_get_remote( GBinderObjectRegistry* reg, guint32 handle, REMOTE_REGISTRY_CREATE create) { return reg ? reg->f->get_remote(reg, handle, create) : NULL; } #endif /* GBINDER_OBJECT_REGISTRY_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_output_data.h000066400000000000000000000050211526127616700207010ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_OUTPUT_DATA_H #define GBINDER_OUTPUT_DATA_H #include "gbinder_types_p.h" typedef struct gbinder_output_data_functions GBinderOutputDataFunctions; struct gbinder_output_data { const GBinderOutputDataFunctions* f; const GByteArray* bytes; }; struct gbinder_output_data_functions { GUtilIntArray* (*offsets)(GBinderOutputData* data); gsize (*buffers_size)(GBinderOutputData* data); }; /* Inline wrappers */ GBINDER_INLINE_FUNC GUtilIntArray* gbinder_output_data_offsets( GBinderOutputData* data) { return data ? data->f->offsets(data) : NULL; } GBINDER_INLINE_FUNC gsize gbinder_output_data_buffers_size( GBinderOutputData* data) { return data ? data->f->buffers_size(data) : 0; } #endif /* GBINDER_OUTPUT_DATA_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_proxy_object.c000066400000000000000000000433041526127616700210600ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2023 Slava Monich * * 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_proxy_object.h" #include "gbinder_local_object_p.h" #include "gbinder_local_request.h" #include "gbinder_local_reply.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_request_p.h" #include "gbinder_remote_reply_p.h" #include "gbinder_object_converter.h" #include "gbinder_object_registry.h" #include "gbinder_rpc_protocol.h" #include "gbinder_driver.h" #include "gbinder_eventloop_p.h" #include "gbinder_ipc.h" #include "gbinder_log.h" #include #include typedef GBinderLocalObjectClass GBinderProxyObjectClass; typedef struct gbinder_proxy_tx GBinderProxyTx; struct gbinder_proxy_tx { GBinderProxyTx* next; GBinderRemoteRequest* req; GBinderProxyObject* proxy; gulong id; }; struct gbinder_proxy_object_priv { gboolean acquired; gboolean dropped; GBinderProxyTx* tx; }; G_DEFINE_TYPE(GBinderProxyObject, gbinder_proxy_object, \ GBINDER_TYPE_LOCAL_OBJECT) #define GBINDER_IS_PROXY_OBJECT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, \ GBINDER_TYPE_PROXY_OBJECT) #define THIS(obj) GBINDER_PROXY_OBJECT(obj) #define THIS_TYPE GBINDER_TYPE_PROXY_OBJECT #define PARENT_CLASS gbinder_proxy_object_parent_class /*==========================================================================* * Converter *==========================================================================*/ typedef struct gbinder_proxy_object_converter { GBinderObjectConverter pub; GBinderIpc* remote; GBinderIpc* local; GBINDER_STABILITY_LEVEL stability; } GBinderProxyObjectConverter; GBINDER_INLINE_FUNC GBinderProxyObjectConverter* gbinder_proxy_object_converter_cast( GBinderObjectConverter* pub) { return G_CAST(pub, GBinderProxyObjectConverter, pub); } static gboolean gbinder_proxy_object_converter_check( GBinderLocalObject* obj, void* remote) { if (GBINDER_IS_PROXY_OBJECT(obj) && THIS(obj)->remote == remote) { /* Found matching proxy object */ return TRUE; } /* Keep looking */ return FALSE; } static void gbinder_proxy_object_set_min_stability( GBinderLocalObject* local, GBINDER_STABILITY_LEVEL stability) { if (local && (local->stability & stability) != stability) { if (local->stability && stability) { local->stability = GBINDER_STABILITY_VINTF; } else { local->stability = stability; } } } static GBinderLocalObject* gbinder_proxy_object_converter_handle_to_local( GBinderObjectConverter* pub, guint32 handle, const GBinderRpcProtocol* protocol, const void* extra) { GBinderProxyObjectConverter* c = gbinder_proxy_object_converter_cast(pub); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(c->remote); GBinderRemoteObject* remote = gbinder_object_registry_get_remote(reg, handle, REMOTE_REGISTRY_CAN_CREATE /* but don't acquire */); GBinderLocalObject* local; /* Retain the original stability before the proxy can be promoted. */ if (protocol->finish_unflatten_binder) { protocol->finish_unflatten_binder(extra, remote); } local = gbinder_ipc_find_local_object(c->local, gbinder_proxy_object_converter_check, remote); if (!local && !remote->dead) { /* GBinderProxyObject will reference GBinderRemoteObject */ local = &gbinder_proxy_object_new(c->local, remote)->parent; } /* The proxy must satisfy both the original object's stability and the * forwarding service's stability. SYSTEM and VENDOR combine as VINTF. */ gbinder_proxy_object_set_min_stability(local, remote->stability); gbinder_proxy_object_set_min_stability(local, c->stability); /* Release the reference returned by gbinder_object_registry_get_remote */ gbinder_remote_object_unref(remote); return local; } static GBinderRemoteObject* gbinder_proxy_object_converter_local_to_remote( GBinderObjectConverter* pub, void* pointer) { GBinderProxyObjectConverter* c = gbinder_proxy_object_converter_cast(pub); GBinderLocalObject* local = gbinder_object_registry_get_local (gbinder_ipc_object_registry(c->remote), pointer); GBinderRemoteObject* remote = NULL; if (local) { if (GBINDER_IS_PROXY_OBJECT(local)) { GBinderRemoteObject* target = THIS(local)->remote; /* Returning a proxy to its original domain restores its handle. */ if (target->ipc == c->local) { remote = gbinder_remote_object_ref(target); } } gbinder_local_object_unref(local); } return remote; } static void gbinder_proxy_object_converter_init( GBinderProxyObjectConverter* convert, GBinderProxyObject* proxy, GBinderIpc* remote, GBinderIpc* local) { static const GBinderObjectConverterFunctions gbinder_converter_fn = { .handle_to_local = gbinder_proxy_object_converter_handle_to_local, .local_to_remote = gbinder_proxy_object_converter_local_to_remote }; GBinderObjectConverter* pub = &convert->pub; GBinderIpc* dest = proxy->parent.ipc; memset(convert, 0, sizeof(*convert)); convert->remote = remote; convert->local = local; convert->stability = proxy->parent.stability; pub->f = &gbinder_converter_fn; pub->io = gbinder_ipc_io(dest); pub->protocol = gbinder_ipc_protocol(dest); } /*==========================================================================* * Implementation *==========================================================================*/ static void gbinder_proxy_object_handle_dead_reply( gpointer user_data) { gbinder_remote_object_commit_suicide(user_data); } static void gbinder_proxy_object_handle_dead_reply_later( GBinderRemoteObject* remote) { gbinder_idle_callback_invoke_later(gbinder_proxy_object_handle_dead_reply, gbinder_remote_object_ref(remote), g_object_unref); } static void gbinder_proxy_tx_dequeue( GBinderProxyTx* tx) { GBinderProxyObject* proxy = tx->proxy; if (proxy) { GBinderProxyObjectPriv* priv = proxy->priv; if (priv->tx) { if (priv->tx == tx) { priv->tx = tx->next; } else { GBinderProxyTx* prev = priv->tx; while (prev->next) { if (prev->next == tx) { prev->next = tx->next; break; } prev = prev->next; } } } tx->next = NULL; tx->proxy = NULL; g_object_unref(proxy); } } static void gbinder_proxy_tx_reply( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { GBinderProxyTx* tx = user_data; GBinderProxyObject* self = tx->proxy; GBinderProxyObjectConverter convert; GBinderLocalReply* fwd; /* * For proxy objects auto-created by the reply, the remote side (the * one sent the reply) will be the remote GBinderIpc and this object's * GBinderIpc will be the local, i.e. those proxies will work in the * same direction as the top level object. The direction gets inverted * twice. */ gbinder_proxy_object_converter_init(&convert, self, ipc, self->parent.ipc); fwd = gbinder_remote_reply_convert_to_local(reply, &convert.pub); tx->id = 0; gbinder_proxy_tx_dequeue(tx); gbinder_remote_request_complete(tx->req, fwd, (status > 0) ? (-EFAULT) : status); if (status == GBINDER_STATUS_DEAD_OBJECT) { /* * Some kernels sometimes don't bother sending us death notifications. * Let's also interpret BR_DEAD_REPLY as an obituary to make sure that * we don't keep dead remote objects around. */ gbinder_remote_object_commit_suicide(self->remote); } gbinder_local_reply_unref(fwd); } static void gbinder_proxy_tx_destroy( gpointer user_data) { GBinderProxyTx* tx = user_data; gbinder_proxy_tx_dequeue(tx); gbinder_remote_request_unref(tx->req); gutil_slice_free(tx); } static GBinderLocalReply* gbinder_proxy_object_handle_transaction( GBinderLocalObject* object, GBinderRemoteRequest* req, guint code, guint flags, int* status) { GBinderProxyObject* self = THIS(object); GBinderProxyObjectPriv* priv = self->priv; GBinderRemoteObject* remote = self->remote; GBinderProxyTx* tx; GBinderLocalRequest* fwd; GBinderProxyObjectConverter convert; if (priv->dropped || remote->dead) { GVERBOSE_("dropped: %d dead:%d", priv->dropped, remote->dead); *status = (-EBADMSG); return NULL; } /* * Direct synchronous transactions don't have req->tx to block * and complete asynchronously. Forward them synchronously and * return the reply directly. */ if (!req->tx) { GBinderRemoteReply* reply; GBinderLocalReply* fwd_reply = NULL; GBinderProxyObjectConverter convert_reply; gboolean has_reply = FALSE; const gboolean oneway = !!(flags & GBINDER_TX_FLAG_ONEWAY); int tx_status; gbinder_proxy_object_converter_init(&convert, self, object->ipc, remote->ipc); fwd = gbinder_remote_request_convert_to_local(req, &convert.pub); if (!fwd) { GWARN("Failed to convert transaction 0x%08x for forwarding", code); *status = -ENOMEM; return NULL; } if (oneway) { reply = NULL; tx_status = gbinder_ipc_transact_sync_oneway_main(remote->ipc, remote->handle, code, fwd); } else { reply = gbinder_ipc_transact_sync_reply_main(remote->ipc, remote->handle, code, fwd, &tx_status); } gbinder_local_request_unref(fwd); if (reply) { has_reply = TRUE; if (gbinder_remote_reply_is_empty(reply)) { fwd_reply = gbinder_local_object_new_reply(object); } else { gbinder_proxy_object_converter_init(&convert_reply, self, remote->ipc, self->parent.ipc); fwd_reply = gbinder_remote_reply_convert_to_local(reply, &convert_reply.pub); } gbinder_remote_reply_unref(reply); } if (tx_status == GBINDER_STATUS_DEAD_OBJECT) { gbinder_proxy_object_handle_dead_reply_later(remote); } *status = (has_reply && !fwd_reply) ? -ENOMEM : tx_status; return fwd_reply; } tx = g_slice_new0(GBinderProxyTx); g_object_ref(tx->proxy = self); tx->req = gbinder_remote_request_ref(req); tx->next = priv->tx; priv->tx = tx; /* Mark the incoming request as pending */ gbinder_remote_request_block(req); /* * For auto-created proxy objects, this object's GBinderIpc will * become a remote, and the remote's GBinderIpc will become local * because they work in the opposite direction. */ gbinder_proxy_object_converter_init(&convert, self, object->ipc, remote->ipc); /* Forward the transaction */ fwd = gbinder_remote_request_convert_to_local(req, &convert.pub); if (fwd) { tx->id = gbinder_ipc_transact(remote->ipc, remote->handle, code, flags, fwd, gbinder_proxy_tx_reply, gbinder_proxy_tx_destroy, tx); gbinder_local_request_unref(fwd); if (tx->id) { *status = GBINDER_STATUS_OK; } else { GWARN("Failed to enqueue forwarded transaction 0x%08x", code); gbinder_proxy_tx_dequeue(tx); gbinder_remote_request_complete(req, NULL, -EFAULT); gbinder_remote_request_unref(tx->req); gutil_slice_free(tx); *status = -EFAULT; } } else { GWARN("Failed to convert transaction 0x%08x for forwarding", code); gbinder_proxy_tx_dequeue(tx); gbinder_remote_request_complete(req, NULL, -ENOMEM); gbinder_remote_request_unref(tx->req); gutil_slice_free(tx); *status = -ENOMEM; } return NULL; } static GBINDER_LOCAL_TRANSACTION_SUPPORT gbinder_proxy_object_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Process all transactions on the main thread */ return GBINDER_LOCAL_TRANSACTION_SUPPORTED; } static void gbinder_proxy_object_acquire( GBinderLocalObject* object) { GBinderProxyObject* self = THIS(object); GBinderProxyObjectPriv* priv = self->priv; GBinderRemoteObject* remote = self->remote; if (!remote->dead && !priv->dropped && !priv->acquired) { /* Not acquired yet */ priv->acquired = TRUE; gbinder_driver_acquire(remote->ipc->driver, remote->handle); } GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->acquire(object); } static void gbinder_proxy_object_drop( GBinderLocalObject* object) { GBinderProxyObject* self = THIS(object); GBinderProxyObjectPriv* priv = self->priv; priv->dropped = TRUE; GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)->drop(object); } /*==========================================================================* * Interface *==========================================================================*/ GBinderProxyObject* gbinder_proxy_object_new( GBinderIpc* src, GBinderRemoteObject* remote) { if (G_LIKELY(remote)) { /* * We don't need to specify the interface list because all * transactions (including HIDL_GET_DESCRIPTOR_TRANSACTION * and HIDL_DESCRIPTOR_CHAIN_TRANSACTION) are getting forwared * to the remote object. */ GBinderLocalObject* object = gbinder_local_object_new_with_type (THIS_TYPE, src, NULL, NULL, NULL); if (object) { GBinderProxyObject* self = THIS(object); object->stability = remote->stability; GDEBUG("Proxy %p %s => %u %s created", self, gbinder_ipc_name(src), remote->handle, gbinder_ipc_name(remote->ipc)); self->remote = gbinder_remote_object_ref(remote); return self; } } return NULL; } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_proxy_object_finalize( GObject* object) { GBinderProxyObject* self = THIS(object); GBinderProxyObjectPriv* priv = self->priv; GBinderLocalObject* local = &self->parent; GBinderRemoteObject* remote = self->remote; /* * gbinder_local_object_finalize() will also try to do the same thing * i.e. invalidate self but proxy objects need to do it before releasing * the handle, to leave no room for race conditions. That's not very good * because it grabs ipc-wide mutex but shouldn'd have much of an impact * on the performance because finalizing a proxy is not supposed to be a * frequent operation. */ gbinder_ipc_invalidate_local_object(local->ipc, local); if (priv->acquired) { gbinder_driver_release(remote->ipc->driver, remote->handle); } GDEBUG("Proxy %p %s => %u %s gone", self, gbinder_ipc_name(self->parent.ipc), remote->handle, gbinder_ipc_name(remote->ipc)); gbinder_remote_object_unref(remote); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_proxy_object_init( GBinderProxyObject* self) { GBinderProxyObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self, THIS_TYPE, GBinderProxyObjectPriv); self->priv = priv; } static void gbinder_proxy_object_class_init( GBinderProxyObjectClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); g_type_class_add_private(klass, sizeof(GBinderProxyObjectPriv)); object_class->finalize = gbinder_proxy_object_finalize; klass->can_handle_transaction = gbinder_proxy_object_can_handle_transaction; klass->handle_transaction = gbinder_proxy_object_handle_transaction; klass->handle_looper_transaction = gbinder_proxy_object_handle_transaction; klass->acquire = gbinder_proxy_object_acquire; klass->drop = gbinder_proxy_object_drop; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_proxy_object.h000066400000000000000000000050221526127616700210600ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * Copyright (C) 2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_PROXY_OBJECT_H #define GBINDER_PROXY_OBJECT_H #include "gbinder_local_object_p.h" typedef struct gbinder_proxy_object_priv GBinderProxyObjectPriv; struct gbinder_proxy_object { GBinderLocalObject parent; GBinderProxyObjectPriv* priv; GBinderRemoteObject* remote; }; GType gbinder_proxy_object_get_type(void) GBINDER_INTERNAL; #define GBINDER_TYPE_PROXY_OBJECT gbinder_proxy_object_get_type() #define GBINDER_PROXY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ GBINDER_TYPE_PROXY_OBJECT, GBinderProxyObject)) /* Registers with src and forwards all transactions to the remote */ GBinderProxyObject* gbinder_proxy_object_new( GBinderIpc* src, GBinderRemoteObject* remote) GBINDER_INTERNAL; #endif /* GBINDER_PROXY_OBJECT_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_reader.c000066400000000000000000000676761526127616700176350ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2024 Jolla Ltd. * * 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_reader_p.h" #include "gbinder_buffer_p.h" #include "gbinder_io.h" #include "gbinder_object_registry.h" #include "gbinder_log.h" #include #include #include /* * GBINDER_READER_FLAG_HAS_PARENT flag means that the objects field * actually points to the top-most GBinderReader's objects pointer, * not to GBinderReaderData's objects area. That way, all nested * readers would share the same objects pointer and that pointer * can be correctly updated even if objects are read by the child * (parcelable) reader. * * +-------+-------+-----+-------+ * | obj_1 | obj_2 | ... | obj_n | tx objects * +-------+-------+-----+-------+ * ^ * | * +-----+ * | * +---------+ * | objects | Top level GBinderReader * +---------+ * ^ * | +---------+ * +----| objects | GBinderReader with a parent * | +---------+ * | * | +---------+ * +----| objects | GBinderReader with a parent * +---------+ */ typedef enum gbinder_reader_flags { GBINDER_READER_FLAG_NONE = 0, GBINDER_READER_FLAG_HAS_PARENT = 0x1, } GBINDER_READER_FLAGS; typedef struct gbinder_reader_priv { const guint8* start; const guint8* end; const guint8* ptr; const GBinderReaderData* data; void** objects; GBINDER_READER_FLAGS flags; } GBinderReaderPriv; G_STATIC_ASSERT(sizeof(GBinderReader) >= sizeof(GBinderReaderPriv)); static inline GBinderReaderPriv* gbinder_reader_cast(GBinderReader* reader) { return (GBinderReaderPriv*)reader; } static inline const GBinderReaderPriv* gbinder_reader_cast_c (const GBinderReader* reader) { return (GBinderReaderPriv*)reader; } static void gbinder_reader_init2( GBinderReader* reader, const GBinderReaderData* data, gsize offset, gsize len, void*** parent_objects) { GBinderReaderPriv* p = gbinder_reader_cast(reader); p->data = data; p->flags = GBINDER_READER_FLAG_NONE; if (G_LIKELY(data)) { GBinderBuffer* buffer = data->buffer; if (buffer) { /* We are assuming that the caller has checked offset and size */ GASSERT(!buffer || (offset + len <= buffer->size)); p->ptr = p->start = (guint8*)buffer->data + offset; p->end = p->ptr + len; } else { p->ptr = p->start = p->end = NULL; } if (parent_objects) { p->objects = (void**)parent_objects; p->flags |= GBINDER_READER_FLAG_HAS_PARENT; } else { p->objects = data->objects; } } else { p->ptr = p->start = p->end = NULL; p->objects = NULL; } } void gbinder_reader_init( GBinderReader* reader, GBinderReaderData* data, gsize offset, gsize len) { gbinder_reader_init2(reader, data, offset, len, NULL); } gboolean gbinder_reader_at_end( const GBinderReader* reader) { const GBinderReaderPriv* p = gbinder_reader_cast_c(reader); return !p || p->ptr >= p->end; } static inline gboolean gbinder_reader_can_read( GBinderReaderPriv* p, gsize len) { return (p->end - p->ptr) >= len; } gboolean gbinder_reader_read_byte( GBinderReader* reader, guchar* value) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (p->ptr < p->end) { if (value) *value = *p->ptr; p->ptr++; return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_bool( GBinderReader* reader, gboolean* value) { /* * Android's libhwbinder writes bool as a single byte and pads it * with zeros, but libbinder writes bool as int32 in native byte * order. The latter becomes either [0x01, 0x00, 0x00, 0x00] or * [0x00, 0x00, 0x00, 0x01] depending on the byte order. Reading * uint32 and comparing it with zero works in either case. */ if (value) { guint32 padded; if (gbinder_reader_read_uint32(reader, &padded)) { *value = (padded != 0); return TRUE; } else { return FALSE; } } else { return gbinder_reader_read_uint32(reader, NULL); } } gboolean gbinder_reader_read_int8( GBinderReader* reader, gint8* value) /* Since 1.1.15 */ { return gbinder_reader_read_uint8(reader, (guint8*)value); } gboolean gbinder_reader_read_uint8( GBinderReader* reader, guint8* value) /* Since 1.1.15 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); /* Primitive values are supposed to be padded to 4-byte boundary */ if (gbinder_reader_can_read(p, 4)) { if (value) { *value = p->ptr[0]; } p->ptr += 4; return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_int16( GBinderReader* reader, gint16* value) /* Since 1.1.15 */ { return gbinder_reader_read_uint16(reader, (guint16*)value); } gboolean gbinder_reader_read_uint16( GBinderReader* reader, guint16* value) /* Since 1.1.15 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); /* Primitive values are supposed to be padded to 4-byte boundary */ if (gbinder_reader_can_read(p, 4)) { if (value) { *value = *(guint16*)p->ptr; } p->ptr += 4; return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_int32( GBinderReader* reader, gint32* value) { return gbinder_reader_read_uint32(reader, (guint32*)value); } gboolean gbinder_reader_read_uint32( GBinderReader* reader, guint32* value) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read(p, sizeof(*value))) { if (value) { const gint32* ptr = (void*)p->ptr; *value = *ptr; } p->ptr += sizeof(*value); return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_int64( GBinderReader* reader, gint64* value) { return gbinder_reader_read_uint64(reader, (guint64*)value); } gboolean gbinder_reader_read_uint64( GBinderReader* reader, guint64* value) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read(p, sizeof(*value))) { if (value) { const gint64* ptr = (void*)p->ptr; *value = *ptr; } p->ptr += sizeof(*value); return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_float( GBinderReader* reader, gfloat* value) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read(p, sizeof(*value))) { if (value) { const gfloat* ptr = (void*)p->ptr; *value = *ptr; } p->ptr += sizeof(*value); return TRUE; } else { return FALSE; } } gboolean gbinder_reader_read_double( GBinderReader* reader, gdouble* value) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read(p, sizeof(*value))) { if (value) { const gdouble* ptr = (void*)p->ptr; *value = *ptr; } p->ptr += sizeof(*value); return TRUE; } else { return FALSE; } } static inline gboolean gbinder_reader_can_read_object( GBinderReaderPriv* p) { const GBinderReaderData* data = p->data; if (data && data->reg) { void** objs = (p->flags & GBINDER_READER_FLAG_HAS_PARENT) ? (*((void***)p->objects)) : p->objects; if (objs && objs[0] == p->ptr) { return TRUE; } } return FALSE; } static inline gboolean gbinder_reader_consume_object( GBinderReaderPriv* p, gsize size) { if (size) { p->ptr += size; if (p->flags & GBINDER_READER_FLAG_HAS_PARENT) { /* Increment the top reader's obj pointer */ (*((void***)p->objects))++; } else { /* We are the top reader */ p->objects++; } return TRUE; } return FALSE; } int gbinder_reader_read_fd( GBinderReader* reader) /* Since 1.0.18 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read_object(p)) { const gsize size = gbinder_reader_bytes_remaining(reader); int fd = -1; if (gbinder_reader_consume_object(p, p->data->reg->io-> decode_fd_object(p->ptr, size, &fd))) { GASSERT(fd >= 0); return fd; } } return -1; } int gbinder_reader_read_dup_fd( GBinderReader* reader) /* Since 1.0.18 */ { const int fd = gbinder_reader_read_fd(reader); if (fd >= 0) { const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (dupfd >= 0) { return dupfd; } else { GWARN("Error dupping fd %d: %s", fd, strerror(errno)); } } return -1; } gboolean gbinder_reader_read_nullable_object( GBinderReader* reader, GBinderRemoteObject** out) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read_object(p)) { const GBinderReaderData* data = p->data; const GBinderRpcProtocol* rpc = gbinder_buffer_protocol(data->buffer); const gsize size = gbinder_reader_bytes_remaining(reader); if (gbinder_reader_consume_object(p, data->reg->io-> decode_binder_object(p->ptr,size, data->reg, out, rpc))) { return TRUE; } } if (out) *out = NULL; return FALSE; } GBinderRemoteObject* gbinder_reader_read_object( GBinderReader* reader) { GBinderRemoteObject* obj = NULL; gbinder_reader_read_nullable_object(reader, &obj); return obj; } static gboolean gbinder_reader_read_buffer_object( GBinderReader* reader, GBinderIoBufferObject* out) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (gbinder_reader_can_read_object(p)) { const GBinderReaderData* data = p->data; GBinderBuffer* buf = data->buffer; const gsize offset = p->ptr - (guint8*)buf->data; if (gbinder_reader_consume_object(p, data->reg->io-> decode_buffer_object(buf, offset, out))) { return TRUE; } } return FALSE; } GBinderBuffer* gbinder_reader_read_buffer( GBinderReader* reader) { GBinderIoBufferObject obj; if (gbinder_reader_read_buffer_object(reader, &obj)) { const GBinderReaderData* data = gbinder_reader_cast(reader)->data; GBinderBuffer* buf = data->buffer; return gbinder_buffer_new_with_parent(buf, obj.data, obj.size); } return NULL; } gboolean gbinder_reader_skip_buffer( GBinderReader* reader) { return gbinder_reader_read_buffer_object(reader, NULL); } /* AIDL Parcelable */ static gboolean gbinder_reader_read_parcelable_header( GBinderReader* reader, guint32* size, gboolean* nullp) { GBinderReaderPriv* p = gbinder_reader_cast(reader); const guint8* saved_ptr = p->ptr; guint32 flag; if (gbinder_reader_read_uint32(reader, &flag)) { if (flag == kNullParcelableFlag) { *size = 0; *nullp = TRUE; return TRUE; } else { guint32 payload_size; if (gbinder_reader_read_uint32(reader, &payload_size) && payload_size >= sizeof(payload_size)) { payload_size -= sizeof(payload_size); if (p->ptr + payload_size <= p->end) { /* Non-NULL parcelable */ *size = payload_size; *nullp = FALSE; return TRUE; } } } } /* Restore the state on failure */ p->ptr = saved_ptr; return FALSE; } const void* gbinder_reader_read_parcelable( GBinderReader* reader, gsize* size) /* Since 1.1.19 */ { /* * Use gbinder_reader_read_parcelable2 if you need to distinguish a failure * to read a parcelable from a successfully read NULL parcelable. */ return gbinder_reader_read_parcelable2(reader, size, NULL); } const void* gbinder_reader_read_parcelable2( GBinderReader* reader, gsize* size, gboolean* ok) /* Since 1.1.48 */ { const void* out = NULL; guint32 psize = 0; gboolean nullp; if (gbinder_reader_read_parcelable_header(reader, &psize, &nullp)) { GBinderReaderPriv* p = gbinder_reader_cast(reader); if (!nullp) { out = p->ptr; p->ptr += psize; /* * If this parcelable contains binder objects we must skip those * objects or else we won't be able to read next objects from this * tx buffer. */ if (p->objects) { guint8*** objs = (guint8***) ((p->flags & GBINDER_READER_FLAG_HAS_PARENT) ? (void***) p->objects : &p->objects); if (*objs) { while ((*objs)[0] && (*objs)[0] < p->ptr) { (*objs)++; } } } } if (ok) { *ok = TRUE; } } else if (ok) { *ok = FALSE; } if (size) { *size = psize; } return out; } gboolean gbinder_reader_start_parcelable( GBinderReader* reader, GBinderReader* parcelable, gboolean* non_null) /* Since 1.1.48 */ { guint32 size; gboolean nullp; /* * gbinder_reader_finish_parcelable() should be invoked on every * parcelable reader initialized by this function. It's especially * important (if not to say mandatory) if the parcelable contains * binder objects. */ if (gbinder_reader_read_parcelable_header(reader, &size, &nullp)) { if (nullp) { /* NULL parcelable (with a parent) */ gbinder_reader_init(parcelable, NULL, 0, 0); gbinder_reader_cast(parcelable)->flags |= GBINDER_READER_FLAG_HAS_PARENT; } else { GBinderReaderPriv* p = gbinder_reader_cast(reader); const GBinderReaderData* data = p->data; gbinder_reader_init2(parcelable, data, (p->ptr - (guint8*)data->buffer->data), size, (p->flags & GBINDER_READER_FLAG_HAS_PARENT) ? (void***)p->objects : &p->objects); /* Move the pointer */ p->ptr += size; } if (non_null) { *non_null = !nullp; } return TRUE; } else { memset(parcelable, 0, sizeof(*parcelable)); if (non_null) { *non_null = FALSE; } return FALSE; } } void gbinder_reader_finish_parcelable( GBinderReader* parcelable) /* Since 1.1.48 */ { GBinderReaderPriv* p = gbinder_reader_cast(parcelable); /* * The reader must be created by gbinder_reader_start_parcelable() * and have a parent but let's check it anyway. */ if (p->flags & GBINDER_READER_FLAG_HAS_PARENT) { /* * If the remaining (unread) part of the parcelable contains binder * objects we must "consume" those objects or else the parent reader * won't be able to read any more objects. */ if (p->objects) { guint8*** objs = (guint8***)p->objects; if (*objs) { while ((*objs)[0] && (*objs)[0] < p->end) { (*objs)++; } } } /* No more reads from this reader */ p->ptr = p->end; } else { GWARN("Invalid reader passed to gbinder_reader_finish_parcelable"); } } gboolean gbinder_reader_skip_parcelable( GBinderReader* reader) /* Since 1.1.49 */ { gboolean ok; gbinder_reader_read_parcelable2(reader, NULL, &ok); return ok; } /* Helper for gbinder_reader_read_hidl_struct() macro */ const void* gbinder_reader_read_hidl_struct1( GBinderReader* reader, gsize size) /* Since 1.0.9 */ { GBinderIoBufferObject obj; if (gbinder_reader_read_buffer_object(reader, &obj) && obj.size == size) { return obj.data; } return NULL; } /* Doesn't copy the data */ const void* gbinder_reader_read_hidl_vec( GBinderReader* reader, gsize* count, gsize* elemsize) { GBinderIoBufferObject obj; const void* out = NULL; gsize out_count = 0, out_elemsize = 0; if (gbinder_reader_read_buffer_object(reader, &obj) && obj.data && obj.size == sizeof(GBinderHidlVec)) { const GBinderHidlVec* vec = obj.data; const void* next = vec->data.ptr; if (next) { if (gbinder_reader_read_buffer_object(reader, &obj) && obj.data == next && ((!vec->count && !obj.size) || (vec->count && obj.size && !(obj.size % vec->count)))) { out_elemsize = vec->count ? (obj.size / vec->count) : 0; out_count = vec->count; out = obj.data; } } else if (!vec->count) { /* Any non-NULL pointer just to indicate success? */ out = vec; } } if (elemsize) { *elemsize = out_elemsize; } if (count) { *count = out_count; } return out; } /* Helper for gbinder_reader_read_hidl_struct_vec() macro */ const void* gbinder_reader_read_hidl_vec1( GBinderReader* reader, gsize* count, guint expected_elem_size) /* Since 1.0.9 */ { gsize actual; const void* data = gbinder_reader_read_hidl_vec(reader, count, &actual); /* Actual size will be zero for an empty array */ return (data && (actual == expected_elem_size || !actual)) ? data : NULL; } const char* gbinder_reader_read_hidl_string_c( GBinderReader* reader) /* Since 1.0.23 */ { GBinderIoBufferObject obj; if (gbinder_reader_read_buffer_object(reader, &obj) && obj.data && obj.size == sizeof(GBinderHidlString)) { const GBinderHidlString* str = obj.data; if (gbinder_reader_read_buffer_object(reader, &obj) && obj.has_parent && obj.parent_offset == GBINDER_HIDL_STRING_BUFFER_OFFSET && obj.data == str->data.str && obj.size == str->len + 1 && str->data.str[str->len] == 0) { return str->data.str; } } return NULL; } char* gbinder_reader_read_hidl_string( GBinderReader* reader) { /* This function should've been called gbinder_reader_dup_hidl_string */ return g_strdup(gbinder_reader_read_hidl_string_c(reader)); } char** gbinder_reader_read_hidl_string_vec( GBinderReader* reader) { GBinderIoBufferObject obj; /* First buffer contains hidl_vector */ if (gbinder_reader_read_buffer_object(reader, &obj) && obj.data && obj.size == sizeof(GBinderHidlVec)) { GBinderHidlVec* vec = obj.data; const guint n = vec->count; const void* next = vec->data.ptr; if (!next && !n) { /* Should this be considered an error? */ return g_new0(char*, 1); } else if (gbinder_reader_read_buffer_object(reader, &obj) && /* The second buffer (if any) contains n hidl_string's */ obj.parent_offset == GBINDER_HIDL_VEC_BUFFER_OFFSET && obj.has_parent && obj.data == next && obj.size == (sizeof(GBinderHidlString) * n)) { const GBinderHidlString* strings = obj.data; GPtrArray* list = g_ptr_array_sized_new(n + 1); guint i; /* Now we expect n buffers containing the actual data */ for (i = 0; i < n && gbinder_reader_read_buffer_object(reader, &obj); i++) { const GBinderHidlString* s = strings + i; const gsize expected_offset = (i * sizeof(*s)) + GBINDER_HIDL_STRING_BUFFER_OFFSET; if (obj.has_parent && obj.parent_offset == expected_offset && obj.data == s->data.str && obj.size == s->len + 1 && s->data.str[s->len] == 0) { char* name = g_strdup(s->data.str); g_ptr_array_add(list, name); GVERBOSE_("%u. %s", i + 1, name); } else { GWARN("Unexpected hidl_string buffer %p/%u vs %p/%u", obj.data, (guint)obj.size, s->data.str, s->len); break; } } if (i == n) { g_ptr_array_add(list, NULL); return (char**)g_ptr_array_free(list, FALSE); } g_ptr_array_set_free_func(list, g_free); g_ptr_array_free(list, TRUE); } } GWARN("Invalid hidl_vec"); return NULL; } /* The equivalent of Android's Parcel::readCString */ const char* gbinder_reader_read_string8( GBinderReader* reader) { GBinderReaderPriv* p = gbinder_reader_cast(reader); const guint8* ptr = p->ptr; /* Calculate the length */ while (ptr < p->end && *ptr) ptr++; if (ptr < p->end) { /* Zero terminator has been found within the bounds */ const gsize len = ptr - p->ptr; const gsize size = G_ALIGN4(len+1); if (p->ptr + size <= p->end) { const char* str = (char*)p->ptr; p->ptr += size; return str; } } return NULL; } /* The equivalent of Android's Parcel::readString8 */ gboolean gbinder_reader_read_nullable_string8( GBinderReader* reader, const char** out, gsize* out_len) /* Since 1.1.41 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); if ((p->ptr + 4) <= p->end) { const gint32* len_ptr = (gint32*)p->ptr; const gint32 len = *len_ptr; if (len == -1) { /* NULL string */ p->ptr += 4; if (out) { *out = NULL; } if (out_len) { *out_len = 0; } return TRUE; } else if (len >= 0 && len < INT32_MAX) { const guint32 padded_len = G_ALIGN4(len + 1); const char* str = (char*)(p->ptr + 4); if ((p->ptr + padded_len + 4) <= p->end && !str[len]) { p->ptr += padded_len + 4; if (out) { *out = str; } if (out_len) { *out_len = len; } return TRUE; } } } return FALSE; } /* The equivalent of Android's Parcel::readString16 */ gboolean gbinder_reader_read_nullable_string16( GBinderReader* reader, char** out) { const gunichar2* str; gsize len; if (gbinder_reader_read_nullable_string16_utf16(reader, &str, &len)) { if (out) { *out = str ? g_utf16_to_utf8(str, len, NULL, NULL, NULL) : NULL; } return TRUE; } return FALSE; } gboolean gbinder_reader_read_nullable_string16_utf16( GBinderReader* reader, const gunichar2** out, gsize* out_len) /* Since 1.0.17 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); if ((p->ptr + 4) <= p->end) { const gint32* len_ptr = (gint32*)p->ptr; const gint32 len = *len_ptr; if (len == -1) { /* NULL string */ p->ptr += 4; if (out) { *out = NULL; } if (out_len) { *out_len = 0; } return TRUE; } else if (len >= 0 && len < INT32_MAX) { const gsize padded_len = G_ALIGN4((((gsize)len) + 1) * 2); if ((p->ptr + padded_len + 4) <= p->end) { const gunichar2* utf16 = (gunichar2*)(p->ptr + 4); if (!utf16[len]) { p->ptr += padded_len + 4; if (out) { *out = utf16; } if (out_len) { *out_len = len; } return TRUE; } } } } return FALSE; } const gunichar2* gbinder_reader_read_string16_utf16( GBinderReader* reader, gsize* len) /* Since 1.0.26 */ { const gunichar2* str; /* * Use gbinder_reader_read_nullable_string16_utf16 to distinguish * NULL string from a parsing failure. */ return gbinder_reader_read_nullable_string16_utf16(reader, &str, len) ? str : NULL; } char* gbinder_reader_read_string16( GBinderReader* reader) { char* str = NULL; gbinder_reader_read_nullable_string16(reader, &str); return str; } gboolean gbinder_reader_skip_string16( GBinderReader* reader) { return gbinder_reader_read_nullable_string16_utf16(reader, NULL, NULL); } const void* gbinder_reader_read_byte_array( GBinderReader* reader, gsize* len) /* Since 1.0.12 */ { GBinderReaderPriv* p = gbinder_reader_cast(reader); const void* data = NULL; const gint32* ptr; *len = 0; if (gbinder_reader_can_read(p, sizeof(*ptr))) { ptr = (void*)p->ptr; if (*ptr <= 0) { p->ptr += sizeof(*ptr); /* Any non-NULL pointer just to indicate success */ data = p->start; } else if (gbinder_reader_can_read(p, sizeof(*ptr) + *ptr)) { *len = (gsize)*ptr; p->ptr += sizeof(*ptr); data = p->ptr; /* Android aligns byte array reads and writes to 4 bytes */ p->ptr += G_ALIGN4(*len); } } return data; } const void* gbinder_reader_get_data( const GBinderReader* reader, gsize* size) /* Since 1.1.14 */ { const GBinderReaderPriv* p = gbinder_reader_cast_c(reader); if (p) { const GBinderReaderData* data = p->data; if (data && data->buffer) { if (size) { *size = data->buffer->size; } return data->buffer->data; } } /* No data */ if (size) { *size = 0; } return NULL; } gsize gbinder_reader_bytes_read( const GBinderReader* reader) { const GBinderReaderPriv* p = gbinder_reader_cast_c(reader); return p ? (p->ptr - p->start) : 0; } gsize gbinder_reader_bytes_remaining( const GBinderReader* reader) { const GBinderReaderPriv* p = gbinder_reader_cast_c(reader); return p ? (p->end - p->ptr) : 0; } void gbinder_reader_copy( GBinderReader* dest, const GBinderReader* src) { if (src) { memcpy(dest, src, sizeof(*dest)); } else { memset(dest, 0, sizeof(*dest)); } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_reader_p.h000066400000000000000000000043101526127616700201310ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_READER_PRIVATE_H #define GBINDER_READER_PRIVATE_H #include #include "gbinder_types_p.h" typedef struct gbinder_reader_data { GBinderBuffer* buffer; GBinderObjectRegistry* reg; void** objects; } GBinderReaderData; void gbinder_reader_init( GBinderReader* reader, GBinderReaderData* data, gsize offset, gsize len) GBINDER_INTERNAL; #endif /* GBINDER_READER_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_object.c000066400000000000000000000237231526127616700211750ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2023 Slava Monich * * 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_driver.h" #include "gbinder_ipc.h" #include "gbinder_remote_object_p.h" #include "gbinder_servicemanager_p.h" #include "gbinder_eventloop_p.h" #include "gbinder_log.h" struct gbinder_remote_object_priv { gboolean acquired; }; typedef GObjectClass GBinderRemoteObjectClass; GType gbinder_remote_object_get_type(void) GBINDER_INTERNAL; G_DEFINE_TYPE(GBinderRemoteObject, gbinder_remote_object, G_TYPE_OBJECT) #define PARENT_CLASS gbinder_remote_object_parent_class #define THIS_TYPE (gbinder_remote_object_get_type()) #define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,THIS_TYPE,GBinderRemoteObject) enum gbinder_remote_object_signal { SIGNAL_DEATH, SIGNAL_COUNT }; #define SIGNAL_DEATH_NAME "death" static guint gbinder_remote_object_signals[SIGNAL_COUNT] = { 0 }; /*==========================================================================* * Implementation *==========================================================================*/ static void gbinder_remote_object_handle_death_on_main_thread( gpointer user_data) { GBinderRemoteObject* self = THIS(user_data); if (!self->dead) { GBinderIpc* ipc = self->ipc; GBinderDriver* driver = ipc->driver; GBinderRemoteObjectPriv* priv = self->priv; self->dead = TRUE; if (priv->acquired) { priv->acquired = FALSE; /* Release the dead node */ gbinder_driver_release(driver, self->handle); } /* ServiceManager always has the same handle, and can be reanimated. */ if (self->handle != GBINDER_SERVICEMANAGER_HANDLE) { gbinder_ipc_invalidate_remote_handle(ipc, self->handle); } gbinder_driver_dead_binder_done(driver, self); g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0); } } /*==========================================================================* * Internal interface *==========================================================================*/ gboolean gbinder_remote_object_reanimate( GBinderRemoteObject* self) { /* * Don't try to reanimate those who hasn't died yet. Reanimation is * kind of a special case and should only be used for servicemanager * objects. */ if (self->dead) { GBinderIpc* ipc = self->ipc; guint32 handle = self->handle; /* Kick the horse */ GASSERT(self->handle == GBINDER_SERVICEMANAGER_HANDLE); if (gbinder_ipc_ping_sync(ipc, handle, &gbinder_ipc_sync_main) == 0) { GBinderRemoteObjectPriv* priv = self->priv; GBinderDriver* driver = ipc->driver; /* Wow, it's alive! */ self->dead = FALSE; priv->acquired = TRUE; gbinder_ipc_looper_check(ipc); /* For death notifications */ gbinder_driver_acquire(driver, handle); gbinder_driver_request_death_notification(driver, self); } } return !self->dead; } void gbinder_remote_object_handle_death_notification( GBinderRemoteObject* self) { /* This function is invoked from the looper thread, the caller has * checked the object pointer */ GVERBOSE_("%p %u", self, self->handle); gbinder_idle_callback_invoke_later (gbinder_remote_object_handle_death_on_main_thread, gbinder_remote_object_ref(self), g_object_unref); } void gbinder_remote_object_commit_suicide( GBinderRemoteObject* self) { /* This function is only invoked by GBinderProxyObject in context of * the main thread, the object pointer is checked by the caller */ if (!self->dead) { GBinderIpc* ipc = self->ipc; GBinderDriver* driver = ipc->driver; GBinderRemoteObjectPriv* priv = self->priv; self->dead = TRUE; gbinder_driver_clear_death_notification(driver, self); if (priv->acquired) { priv->acquired = FALSE; /* Release the dead node */ gbinder_driver_release(driver, self->handle); } GVERBOSE_("%p %u", self, self->handle); gbinder_ipc_invalidate_remote_handle(self->ipc, self->handle); /* Don't submit BC_DEAD_BINDER_DONE because this is a suicide */ g_signal_emit(self, gbinder_remote_object_signals[SIGNAL_DEATH], 0); } } /*==========================================================================* * Interface *==========================================================================*/ GBinderRemoteObject* gbinder_remote_object_new( GBinderIpc* ipc, guint32 handle, REMOTE_OBJECT_CREATE create) { if (G_LIKELY(ipc)) { GBinderRemoteObject* self = g_object_new(THIS_TYPE, NULL); GBinderRemoteObjectPriv* priv = self->priv; self->ipc = gbinder_ipc_ref(ipc); self->handle = handle; self->stability = GBINDER_STABILITY_SYSTEM; switch (create) { case REMOTE_OBJECT_CREATE_DEAD: self->dead = TRUE; break; case REMOTE_OBJECT_CREATE_ACQUIRED: priv->acquired = TRUE; /* fallthrough */ case REMOTE_OBJECT_CREATE_ALIVE: break; } if (!self->dead) { gbinder_ipc_looper_check(self->ipc); /* For death notifications */ if (priv->acquired) { gbinder_driver_acquire(ipc->driver, handle); } gbinder_driver_request_death_notification(ipc->driver, self); } return self; } return NULL; } GBinderRemoteObject* gbinder_remote_object_ref( GBinderRemoteObject* self) { if (G_LIKELY(self)) { g_object_ref(THIS(self)); return self; } else { return NULL; } } void gbinder_remote_object_unref( GBinderRemoteObject* self) { if (G_LIKELY(self)) { g_object_unref(THIS(self)); } } GBinderIpc* gbinder_remote_object_ipc( GBinderRemoteObject* self) /* Since 1.0.30 */ { return G_LIKELY(self) ? self->ipc : NULL; } gboolean gbinder_remote_object_is_dead( GBinderRemoteObject* self) { return G_UNLIKELY(!self) || self->dead; } gulong gbinder_remote_object_add_death_handler( GBinderRemoteObject* self, GBinderRemoteObjectNotifyFunc fn, void* data) { if (G_LIKELY(self) && G_LIKELY(fn)) { /* To receive the notifications, we need to have looper running */ gbinder_ipc_looper_check(self->ipc); return g_signal_connect(self, SIGNAL_DEATH_NAME, G_CALLBACK(fn), data); } return 0; } void gbinder_remote_object_remove_handler( GBinderRemoteObject* self, gulong id) { if (G_LIKELY(self) && G_LIKELY(id)) { g_signal_handler_disconnect(self, id); } } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_remote_object_init( GBinderRemoteObject* self) { GBinderRemoteObjectPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self, THIS_TYPE, GBinderRemoteObjectPriv); self->priv = priv; } static void gbinder_remote_object_dispose( GObject* object) { GBinderRemoteObject* self = THIS(object); gbinder_ipc_remote_object_disposed(self->ipc, self); G_OBJECT_CLASS(PARENT_CLASS)->dispose(object); } static void gbinder_remote_object_finalize( GObject* object) { GBinderRemoteObject* self = THIS(object); GBinderRemoteObjectPriv* priv = self->priv; GBinderIpc* ipc = self->ipc; GBinderDriver* driver = ipc->driver; gbinder_ipc_invalidate_remote_handle(ipc, self->handle); if (!self->dead) { gbinder_driver_clear_death_notification(driver, self); } if (priv->acquired) { gbinder_driver_release(driver, self->handle); } gbinder_ipc_unref(ipc); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_remote_object_class_init( GBinderRemoteObjectClass* klass) { GObjectClass* remote_class = G_OBJECT_CLASS(klass); g_type_class_add_private(klass, sizeof(GBinderRemoteObjectPriv)); remote_class->dispose = gbinder_remote_object_dispose; remote_class->finalize = gbinder_remote_object_finalize; gbinder_remote_object_signals[SIGNAL_DEATH] = g_signal_new(SIGNAL_DEATH_NAME, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_object_p.h000066400000000000000000000057531526127616700215240ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_OBJECT_PRIVATE_H #define GBINDER_REMOTE_OBJECT_PRIVATE_H #include #include "gbinder_types_p.h" #include typedef struct gbinder_remote_object_priv GBinderRemoteObjectPriv; struct gbinder_remote_object { GObject object; GBinderRemoteObjectPriv* priv; GBinderIpc* ipc; guint32 handle; GBINDER_STABILITY_LEVEL stability; gboolean dead; }; #define gbinder_remote_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver)) #define gbinder_remote_object_io(obj) (gbinder_driver_io((obj)->ipc->driver)) typedef enum gbinder_remote_object_create { REMOTE_OBJECT_CREATE_DEAD, REMOTE_OBJECT_CREATE_ALIVE, REMOTE_OBJECT_CREATE_ACQUIRED } REMOTE_OBJECT_CREATE; GBinderRemoteObject* gbinder_remote_object_new( GBinderIpc* ipc, guint32 handle, REMOTE_OBJECT_CREATE create) GBINDER_INTERNAL; gboolean gbinder_remote_object_reanimate( GBinderRemoteObject* obj) GBINDER_INTERNAL; void gbinder_remote_object_handle_death_notification( GBinderRemoteObject* obj) GBINDER_INTERNAL; void gbinder_remote_object_commit_suicide( GBinderRemoteObject* self) GBINDER_INTERNAL; #endif /* GBINDER_REMOTE_OBJECT_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_reply.c000066400000000000000000000146641526127616700210660ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * 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_remote_reply_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_reader_p.h" #include "gbinder_object_registry.h" #include "gbinder_buffer_p.h" #include "gbinder_log.h" #include struct gbinder_remote_reply { gint refcount; GBinderReaderData data; }; GBinderRemoteReply* gbinder_remote_reply_new( GBinderObjectRegistry* reg) { GBinderRemoteReply* self = g_slice_new0(GBinderRemoteReply); GBinderReaderData* data = &self->data; g_atomic_int_set(&self->refcount, 1); data->reg = gbinder_object_registry_ref(reg); return self; } static void gbinder_remote_reply_free( GBinderRemoteReply* self) { GBinderReaderData* data = &self->data; gbinder_object_registry_unref(data->reg); gbinder_buffer_free(data->buffer); g_slice_free(GBinderRemoteReply, self); } void gbinder_remote_reply_set_data( GBinderRemoteReply* self, GBinderBuffer* buffer) { /* The caller checks the pointer for NULL */ GBinderReaderData* data = &self->data; gbinder_buffer_free(data->buffer); data->buffer = buffer; data->objects = gbinder_buffer_objects(buffer); } GBinderRemoteReply* gbinder_remote_reply_ref( GBinderRemoteReply* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return self; } void gbinder_remote_reply_unref( GBinderRemoteReply* self) { if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_remote_reply_free(self); } } } gboolean gbinder_remote_reply_is_empty( GBinderRemoteReply* self) { return !self || !self->data.buffer || !self->data.buffer->size; } GBinderLocalReply* gbinder_remote_reply_copy_to_local( GBinderRemoteReply* self) { return gbinder_remote_reply_convert_to_local(self, NULL); } GBinderLocalReply* gbinder_remote_reply_convert_to_local( GBinderRemoteReply* self, GBinderObjectConverter* convert) { if (G_LIKELY(self)) { GBinderReaderData* d = &self->data; GBinderObjectRegistry* reg = d->reg; if (reg) { return gbinder_local_reply_set_contents(gbinder_local_reply_new (reg->io, gbinder_buffer_protocol(d->buffer)), d->buffer, convert); } } return NULL; } static inline void gbinder_remote_reply_init_reader2( GBinderRemoteReply* self, GBinderReader* p) { /* The caller has already checked the reply for NULL */ GBinderReaderData* data = &self->data; GBinderBuffer* buffer = data->buffer; if (buffer) { gbinder_reader_init(p, data, 0, buffer->size); } else { gbinder_reader_init(p, data, 0, 0); } } void gbinder_remote_reply_init_reader( GBinderRemoteReply* self, GBinderReader* reader) { if (G_LIKELY(self)) { gbinder_remote_reply_init_reader2(self, reader); } else { gbinder_reader_init(reader, NULL, 0, 0); } } gboolean gbinder_remote_reply_read_int32( GBinderRemoteReply* self, gint32* value) { return gbinder_remote_reply_read_uint32(self, (guint32*)value); } gboolean gbinder_remote_reply_read_uint32( GBinderRemoteReply* self, guint32* value) { if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_reply_init_reader2(self, &reader); return gbinder_reader_read_uint32(&reader, value); } return FALSE; } gboolean gbinder_remote_reply_read_int64( GBinderRemoteReply* self, gint64* value) { return gbinder_remote_reply_read_uint64(self, (guint64*)value); } gboolean gbinder_remote_reply_read_uint64( GBinderRemoteReply* self, guint64* value) { if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_reply_init_reader2(self, &reader); return gbinder_reader_read_uint64(&reader, value); } return FALSE; } const char* gbinder_remote_reply_read_string8( GBinderRemoteReply* self) { if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_reply_init_reader2(self, &reader); return gbinder_reader_read_string8(&reader); } return NULL; } char* gbinder_remote_reply_read_string16( GBinderRemoteReply* self) { if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_reply_init_reader2(self, &reader); return gbinder_reader_read_string16(&reader); } return NULL; } GBinderRemoteObject* gbinder_remote_reply_read_object( GBinderRemoteReply* self) { if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_reply_init_reader2(self, &reader); return gbinder_reader_read_object(&reader); } return NULL; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_reply_p.h000066400000000000000000000046311526127616700214030ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_REPLY_PRIVATE_H #define GBINDER_REMOTE_REPLY_PRIVATE_H #include #include "gbinder_types_p.h" GBinderRemoteReply* gbinder_remote_reply_new( GBinderObjectRegistry* reg) GBINDER_INTERNAL; GBinderLocalReply* gbinder_remote_reply_convert_to_local( GBinderRemoteReply* reply, GBinderObjectConverter* convert) GBINDER_INTERNAL; void gbinder_remote_reply_set_data( GBinderRemoteReply* reply, GBinderBuffer* buffer) GBINDER_INTERNAL; gboolean gbinder_remote_reply_is_empty( GBinderRemoteReply* reply) GBINDER_INTERNAL; #endif /* GBINDER_REMOTE_REPLY_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_request.c000066400000000000000000000232571526127616700214210ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * 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_remote_request_p.h" #include "gbinder_reader_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_local_request_p.h" #include "gbinder_object_converter.h" #include "gbinder_object_registry.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_log.h" #include #include typedef struct gbinder_remote_request_priv { GBinderRemoteRequest pub; gint refcount; pid_t pid; uid_t euid; const GBinderRpcProtocol* protocol; const char* iface; char* iface2; gsize header_size; GBinderReaderData data; } GBinderRemoteRequestPriv; GBINDER_INLINE_FUNC GBinderRemoteRequestPriv* gbinder_remote_request_cast(GBinderRemoteRequest* pub) { return G_LIKELY(pub) ? G_CAST(pub,GBinderRemoteRequestPriv,pub) : NULL; } GBinderRemoteRequest* gbinder_remote_request_new( GBinderObjectRegistry* reg, const GBinderRpcProtocol* protocol, pid_t pid, uid_t euid) { GBinderRemoteRequestPriv* self = g_slice_new0(GBinderRemoteRequestPriv); GBinderReaderData* data = &self->data; g_atomic_int_set(&self->refcount, 1); self->pid = pid; self->euid = euid; self->protocol = protocol; data->reg = gbinder_object_registry_ref(reg); return &self->pub; } GBinderLocalRequest* gbinder_remote_request_copy_to_local( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReaderData* d = &self->data; return gbinder_local_request_new_from_data(d->buffer, NULL); } return NULL; } GBinderLocalRequest* gbinder_remote_request_convert_to_local( GBinderRemoteRequest* req, GBinderObjectConverter* convert) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self) && G_LIKELY(convert)) { GBinderReaderData* data = &self->data; GBinderBuffer* buffer = data->buffer; if (!buffer) { return gbinder_local_request_new(convert->io, convert->protocol, NULL); } else if (convert->protocol == self->protocol) { /* The same protocol, the same format of RPC header */ return gbinder_local_request_new_from_data(buffer, convert); } else { /* Need to translate to another format */ GBinderLocalRequest* local = gbinder_local_request_new_iface (convert->io, convert->protocol, self->iface); gbinder_local_request_append_contents(local, buffer, self->header_size, convert); return local; } } return NULL; } static void gbinder_remote_request_free( GBinderRemoteRequestPriv* self) { GBinderReaderData* data = &self->data; GBinderRemoteRequest* req = &self->pub; GASSERT(!req->tx); if (req->tx) { GWARN("Request is dropped without completing the transaction"); gbinder_remote_request_complete(req, NULL, -ECANCELED); } gbinder_object_registry_unref(data->reg); gbinder_buffer_free(data->buffer); g_free(self->iface2); g_slice_free(GBinderRemoteRequestPriv, self); } static inline void gbinder_remote_request_init_reader2( GBinderRemoteRequestPriv* self, GBinderReader* p) { /* The caller has already checked the request for NULL */ GBinderReaderData* data = &self->data; GBinderBuffer* buffer = data->buffer; if (buffer) { gbinder_reader_init(p, data, self->header_size, buffer->size - self->header_size); } else { gbinder_reader_init(p, data, 0, 0); } } void gbinder_remote_request_set_data( GBinderRemoteRequest* req, guint32 txcode, GBinderBuffer* buffer) { /* The caller never passes NULL req */ GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); GBinderReaderData* data = &self->data; GBinderReader reader; g_free(self->iface2); gbinder_buffer_free(data->buffer); data->buffer = buffer; data->objects = gbinder_buffer_objects(buffer); /* Parse RPC header */ gbinder_remote_request_init_reader2(self, &reader); self->iface = self->protocol->read_rpc_header(&reader, txcode, &self->iface2); if (self->iface) { self->header_size = gbinder_reader_bytes_read(&reader); } else { /* No RPC header */ self->header_size = 0; } } const char* gbinder_remote_request_interface( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); return G_LIKELY(self) ? self->iface : NULL; } GBinderRemoteRequest* gbinder_remote_request_ref( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GASSERT(self->refcount > 0); g_atomic_int_inc(&self->refcount); } return req; } void gbinder_remote_request_unref( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GASSERT(self->refcount > 0); if (g_atomic_int_dec_and_test(&self->refcount)) { gbinder_remote_request_free(self); } } } void gbinder_remote_request_init_reader( GBinderRemoteRequest* req, GBinderReader* reader) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { gbinder_remote_request_init_reader2(self, reader); } else { gbinder_reader_init(reader, NULL, 0, 0); } } pid_t gbinder_remote_request_sender_pid( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); return G_LIKELY(self) ? self->pid : (uid_t)(-1); } uid_t gbinder_remote_request_sender_euid( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); return G_LIKELY(self) ? self->euid : (uid_t)(-1); } gboolean gbinder_remote_request_read_int32( GBinderRemoteRequest* self, gint32* value) { return gbinder_remote_request_read_uint32(self, (guint32*)value); } gboolean gbinder_remote_request_read_uint32( GBinderRemoteRequest* req, guint32* value) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_request_init_reader2(self, &reader); return gbinder_reader_read_uint32(&reader, value); } return FALSE; } gboolean gbinder_remote_request_read_int64( GBinderRemoteRequest* self, gint64* value) { return gbinder_remote_request_read_uint64(self, (guint64*)value); } gboolean gbinder_remote_request_read_uint64( GBinderRemoteRequest* req, guint64* value) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_request_init_reader2(self, &reader); return gbinder_reader_read_uint64(&reader, value); } return FALSE; } const char* gbinder_remote_request_read_string8( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_request_init_reader2(self, &reader); return gbinder_reader_read_string8(&reader); } return NULL; } char* gbinder_remote_request_read_string16( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_request_init_reader2(self, &reader); return gbinder_reader_read_string16(&reader); } return NULL; } GBinderRemoteObject* gbinder_remote_request_read_object( GBinderRemoteRequest* req) { GBinderRemoteRequestPriv* self = gbinder_remote_request_cast(req); if (G_LIKELY(self)) { GBinderReader reader; gbinder_remote_request_init_reader2(self, &reader); return gbinder_reader_read_object(&reader); } return NULL; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_remote_request_p.h000066400000000000000000000047521526127616700217440ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_REMOTE_REQUEST_PRIVATE_H #define GBINDER_REMOTE_REQUEST_PRIVATE_H #include #include "gbinder_types_p.h" struct gbinder_remote_request { GBinderIpcLooperTx* tx; }; GBinderRemoteRequest* gbinder_remote_request_new( GBinderObjectRegistry* reg, const GBinderRpcProtocol* protocol, pid_t pid, uid_t euid) GBINDER_INTERNAL; void gbinder_remote_request_set_data( GBinderRemoteRequest* request, guint txcode, GBinderBuffer* buffer) GBINDER_INTERNAL; GBinderLocalRequest* gbinder_remote_request_convert_to_local( GBinderRemoteRequest* req, GBinderObjectConverter* convert) GBINDER_INTERNAL; #endif /* GBINDER_REMOTE_REQUEST_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_rpc_protocol.c000066400000000000000000000464061526127616700210640ustar00rootroot00000000000000/* * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2025 Jolla Mobile Ltd. * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_fmq_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_reader.h" #include "gbinder_writer_p.h" #include "gbinder_config.h" #include "gbinder_log.h" #include "gbinder_local_object_p.h" #include "gbinder_remote_object_p.h" #include #include #define STRICT_MODE_PENALTY_GATHER (0x40 << 16) #define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER) #define UNSET_WORK_SOURCE (-1) #define BINDER_VND_HEADER GBINDER_FOURCC('V', 'N', 'D', 'R') #define BINDER_SYS_HEADER GBINDER_FOURCC('S', 'Y', 'S', 'T') /*==========================================================================* * GBinderIpcProtocol callbacks (see Parcel::writeInterfaceToken in Android) * Note that there are two slightly different kinds of Parcels: * * platform/system/libhwbinder/Parcel.cpp * platform/frameworks/native/libs/binder/Parcel.cpp * * which mutate from version to version. Specific device => protocol * mapping can be optionally configured in /etc/gbinder.conf file. * The default protocol configuration looks like this: * * [Protocol] * Default = aidl * /dev/binder = aidl * /dev/hwbinder = hidl * *==========================================================================*/ #define CONF_GROUP GBINDER_CONFIG_GROUP_PROTOCOL #define CONF_DEFAULT GBINDER_CONFIG_VALUE_DEFAULT static GHashTable* gbinder_rpc_protocol_map = NULL; /* * Default protocol for those binder devices which which haven't been * explicitely mapped. */ #define DEFAULT_PROTOCOL gbinder_rpc_protocol_aidl static const GBinderRpcProtocol DEFAULT_PROTOCOL; static const GBinderRpcProtocol* gbinder_rpc_protocol_default = &DEFAULT_PROTOCOL; /*==========================================================================* * Common AIDL protocol *==========================================================================*/ static void gbinder_rpc_protocol_aidl_write_fmq_grantor_descriptor( GBinderWriter* writer, const GBinderFmqGrantorDescriptor* grantor) { GBinderWriter parcelable; /* * package android.hardware.common.fmq; * parcelable GrantorDescriptor { * int fdIndex; * int offset; * long extent; * } */ gbinder_writer_start_parcelable(writer, &parcelable); gbinder_writer_append_int32(&parcelable, grantor->fd_index); gbinder_writer_append_int32(&parcelable, grantor->offset); gbinder_writer_append_int64(&parcelable, grantor->extent); gbinder_writer_finish_parcelable(&parcelable); } static void gbinder_rpc_protocol_aidl_write_fds( GBinderWriter* writer, const GBinderFds* fds) { GBinderWriter parcelable; guint i; /* * package android.hardware.common; * parcelable NativeHandle { * ParcelFileDescriptor[] fds; * int[] ints; * } */ gbinder_writer_start_parcelable(writer, &parcelable); /* fds */ gbinder_writer_append_int32(&parcelable, fds->num_fds); for (i = 0; i < fds->num_fds; i++) { gbinder_writer_append_int32(&parcelable, 1); gbinder_writer_append_int32(&parcelable, 0); gbinder_writer_append_fd(&parcelable, gbinder_fds_get_fd(fds, i)); } /* ints */ gbinder_writer_append_int32(&parcelable, fds->num_ints); for (i = 0; i < fds->num_ints; i++) { gbinder_writer_append_int32(&parcelable, gbinder_fds_get_fd(fds, fds->num_fds + i)); } gbinder_writer_finish_parcelable(&parcelable); } static void gbinder_rpc_protocol_aidl_write_fmq_descriptor( GBinderWriter* writer, const GBinderFmq* queue) { if (queue) { const GBinderMQDescriptor* desc = gbinder_fmq_get_descriptor(queue); const GBinderFmqGrantorDescriptor* grantors = desc->grantors.data.ptr; GBinderWriter parcelable; guint i; /* * package android.hardware.common.fmq; * parcelable MQDescriptor { * android.hardware.common.fmq.GrantorDescriptor[] grantors; * android.hardware.common.NativeHandle handle; * int quantum; * int flags; * } */ gbinder_writer_start_parcelable(writer, &parcelable); /* grantors */ gbinder_writer_append_int32(&parcelable, desc->grantors.count); for (i = 0; i < desc->grantors.count; i++) { gbinder_rpc_protocol_aidl_write_fmq_grantor_descriptor (&parcelable, grantors + i); } gbinder_rpc_protocol_aidl_write_fds(&parcelable, desc->data.fds); gbinder_writer_append_int32(&parcelable, desc->quantum); gbinder_writer_append_int32(&parcelable, desc->flags); gbinder_writer_finish_parcelable(&parcelable); } else { gbinder_writer_append_null_parcelable(writer); } } /*==========================================================================* * The original AIDL protocol. *==========================================================================*/ static void gbinder_rpc_protocol_aidl_write_ping( GBinderWriter* writer) { /* No payload */ } static void gbinder_rpc_protocol_aidl_write_rpc_header( GBinderWriter* writer, const char* iface) { /* * writeInt32(IPCThreadState::self()->getStrictModePolicy() | * STRICT_MODE_PENALTY_GATHER); * writeString16(interface); */ gbinder_writer_append_int32(writer, BINDER_RPC_FLAGS); gbinder_writer_append_string16(writer, iface); } static const char* gbinder_rpc_protocol_aidl_read_rpc_header( GBinderReader* reader, guint32 txcode, char** iface) { if (txcode > GBINDER_TRANSACTION(0,0,0)) { /* Internal transaction e.g. GBINDER_DUMP_TRANSACTION etc. */ *iface = NULL; } else if (gbinder_reader_read_int32(reader, NULL)) { *iface = gbinder_reader_read_string16(reader); } else { *iface = NULL; } return *iface; } static const GBinderRpcProtocol gbinder_rpc_protocol_aidl = { .name = "aidl", .ping_tx = GBINDER_PING_TRANSACTION, .write_ping = gbinder_rpc_protocol_aidl_write_ping, .write_rpc_header = gbinder_rpc_protocol_aidl_write_rpc_header, .read_rpc_header = gbinder_rpc_protocol_aidl_read_rpc_header, .write_fmq_descriptor = gbinder_rpc_protocol_aidl_write_fmq_descriptor, }; /*==========================================================================* * AIDL protocol appeared in Android 10 (API level 29) *==========================================================================*/ static void gbinder_rpc_protocol_aidl2_write_rpc_header( GBinderWriter* writer, const char* iface) { /* * writeInt32(IPCThreadState::self()->getStrictModePolicy() | * STRICT_MODE_PENALTY_GATHER); * writeInt32(IPCThreadState::kUnsetWorkSource); * writeString16(interface); */ gbinder_writer_append_int32(writer, BINDER_RPC_FLAGS); gbinder_writer_append_int32(writer, UNSET_WORK_SOURCE); gbinder_writer_append_string16(writer, iface); } static const char* gbinder_rpc_protocol_aidl2_read_rpc_header( GBinderReader* reader, guint32 txcode, char** iface) { if (txcode > GBINDER_TRANSACTION(0,0,0)) { /* Internal transaction e.g. GBINDER_DUMP_TRANSACTION etc. */ *iface = NULL; } else if (gbinder_reader_read_int32(reader, NULL) /* flags */ && gbinder_reader_read_int32(reader, NULL) /* work source */) { *iface = gbinder_reader_read_string16(reader); } else { *iface = NULL; } return *iface; } static const GBinderRpcProtocol gbinder_rpc_protocol_aidl2 = { .name = "aidl2", .ping_tx = GBINDER_PING_TRANSACTION, .write_ping = gbinder_rpc_protocol_aidl_write_ping, /* no payload */ .write_rpc_header = gbinder_rpc_protocol_aidl2_write_rpc_header, .read_rpc_header = gbinder_rpc_protocol_aidl2_read_rpc_header, .write_fmq_descriptor = gbinder_rpc_protocol_aidl_write_fmq_descriptor, }; /*==========================================================================* * AIDL protocol appeared in Android 11 (API level 30) *==========================================================================*/ static void gbinder_rpc_protocol_aidl3_write_rpc_header( GBinderWriter* writer, const char* iface) { gbinder_writer_append_int32(writer, BINDER_RPC_FLAGS); gbinder_writer_append_int32(writer, UNSET_WORK_SOURCE); gbinder_writer_append_int32(writer, BINDER_SYS_HEADER); gbinder_writer_append_string16(writer, iface); } static const char* gbinder_rpc_protocol_aidl3_read_rpc_header( GBinderReader* reader, guint32 txcode, char** iface) { if (txcode > GBINDER_TRANSACTION(0,0,0)) { *iface = NULL; } else if (gbinder_reader_read_int32(reader, NULL) /* flags */ && gbinder_reader_read_int32(reader, NULL) /* work source */ && gbinder_reader_read_int32(reader, NULL) /* sys header */) { *iface = gbinder_reader_read_string16(reader); } else { *iface = NULL; } return *iface; } static void gbinder_rpc_protocol_aidl3_finish_flatten_binder( void* out, GBINDER_STABILITY_LEVEL stability) { *(guint32*)out = stability; } static void gbinder_rpc_protocol_aidl3_finish_unflatten_binder( const void* in, GBinderRemoteObject* obj) { guint32 stability; memcpy(&stability, in, sizeof(stability)); obj->stability = stability; } static const GBinderRpcProtocol gbinder_rpc_protocol_aidl3 = { .name = "aidl3", .ping_tx = GBINDER_PING_TRANSACTION, .write_ping = gbinder_rpc_protocol_aidl_write_ping, /* no payload */ .write_rpc_header = gbinder_rpc_protocol_aidl3_write_rpc_header, .read_rpc_header = gbinder_rpc_protocol_aidl3_read_rpc_header, .flat_binder_object_extra = 4, .finish_flatten_binder = gbinder_rpc_protocol_aidl3_finish_flatten_binder, .finish_unflatten_binder = gbinder_rpc_protocol_aidl3_finish_unflatten_binder, .write_fmq_descriptor = gbinder_rpc_protocol_aidl_write_fmq_descriptor, }; /*==========================================================================* * AIDL protocol appeared in Android 12 (API level 31), but reverted in * Android 13 (API level 33). *==========================================================================*/ #define BINDER_WIRE_FORMAT_VERSION_AIDL4 1 struct stability_category { guint8 binder_wire_format_version; guint8 reserved[2]; guint8 stability_level; }; G_STATIC_ASSERT(sizeof(struct stability_category) == sizeof(guint32)); static void gbinder_rpc_protocol_aidl4_finish_flatten_binder( void* out, GBINDER_STABILITY_LEVEL stability) { struct stability_category cat = { .binder_wire_format_version = BINDER_WIRE_FORMAT_VERSION_AIDL4, .reserved = { 0, 0, }, .stability_level = stability, }; memcpy(out, &cat, sizeof(cat)); } static void gbinder_rpc_protocol_aidl4_finish_unflatten_binder( const void* in, GBinderRemoteObject* obj) { struct stability_category cat; memcpy(&cat, in, sizeof(cat)); obj->stability = cat.stability_level; } static const GBinderRpcProtocol gbinder_rpc_protocol_aidl4 = { .name = "aidl4", .ping_tx = GBINDER_PING_TRANSACTION, .write_ping = gbinder_rpc_protocol_aidl_write_ping, /* no payload */ .write_rpc_header = gbinder_rpc_protocol_aidl3_write_rpc_header, .read_rpc_header = gbinder_rpc_protocol_aidl3_read_rpc_header, .flat_binder_object_extra = 4, .finish_flatten_binder = gbinder_rpc_protocol_aidl4_finish_flatten_binder, .finish_unflatten_binder = gbinder_rpc_protocol_aidl4_finish_unflatten_binder, .write_fmq_descriptor = gbinder_rpc_protocol_aidl_write_fmq_descriptor, }; /*==========================================================================* * The original /dev/hwbinder protocol. *==========================================================================*/ static void gbinder_rpc_protocol_hidl_write_rpc_header( GBinderWriter* writer, const char* iface) { /* * writeCString(interface); */ gbinder_writer_append_string8(writer, iface); } static void gbinder_rpc_protocol_hidl_write_ping( GBinderWriter* writer) { gbinder_rpc_protocol_hidl_write_rpc_header(writer, "android.hidl.base@1.0::IBase"); } static const char* gbinder_rpc_protocol_hidl_read_rpc_header( GBinderReader* reader, guint32 txcode, char** iface) { *iface = NULL; return gbinder_reader_read_string8(reader); } void gbinder_rpc_protocol_hidl_write_fmq_descriptor( GBinderWriter* writer, const GBinderFmq* queue) { GBinderParent parent; GBinderMQDescriptor* desc = gbinder_fmq_get_descriptor(queue); GBinderMQDescriptor* mqdesc = gutil_memdup(desc, sizeof(GBinderMQDescriptor)); const gsize vec_total = desc->grantors.count * sizeof(GBinderFmqGrantorDescriptor); void* vec_buf = gutil_memdup(desc->grantors.data.ptr, vec_total); const gsize fds_total = sizeof(GBinderFds) + sizeof(int) * (desc->data.fds->num_fds + desc->data.fds->num_ints); GBinderFds* fds = gutil_memdup(desc->data.fds, fds_total); mqdesc->data.fds = fds; gbinder_writer_add_cleanup(writer, g_free, mqdesc); gbinder_writer_add_cleanup(writer, g_free, fds); /* Fill in the grantor vector descriptor */ if (vec_buf) { mqdesc->grantors.count = desc->grantors.count; mqdesc->grantors.data.ptr = vec_buf; mqdesc->grantors.owns_buffer = TRUE; gbinder_writer_add_cleanup(writer, g_free, vec_buf); } /* Write the FMQ descriptor object */ parent.index = gbinder_writer_append_buffer_object(writer, mqdesc, sizeof(*mqdesc)); /* Write the vector data buffer */ parent.offset = GBINDER_MQ_DESCRIPTOR_GRANTORS_OFFSET; gbinder_writer_append_buffer_object_with_parent(writer, vec_buf, vec_total, &parent); /* Write the fds */ parent.offset = GBINDER_MQ_DESCRIPTOR_FDS_OFFSET; gbinder_writer_append_fds(writer, mqdesc->data.fds, &parent); } static const GBinderRpcProtocol gbinder_rpc_protocol_hidl = { .name = "hidl", .ping_tx = HIDL_PING_TRANSACTION, .write_ping = gbinder_rpc_protocol_hidl_write_ping, .write_rpc_header = gbinder_rpc_protocol_hidl_write_rpc_header, .read_rpc_header = gbinder_rpc_protocol_hidl_read_rpc_header, .write_fmq_descriptor = gbinder_rpc_protocol_hidl_write_fmq_descriptor, }; /*==========================================================================* * Implementation *==========================================================================*/ /* All known protocols */ static const GBinderRpcProtocol* gbinder_rpc_protocol_list[] = { &gbinder_rpc_protocol_aidl, &gbinder_rpc_protocol_aidl2, &gbinder_rpc_protocol_aidl3, &gbinder_rpc_protocol_aidl4, &gbinder_rpc_protocol_hidl }; static const GBinderRpcProtocol* gbinder_rpc_protocol_find( const char* name) { guint i; for (i = 0; i < G_N_ELEMENTS(gbinder_rpc_protocol_list); i++) { if (!g_ascii_strcasecmp(gbinder_rpc_protocol_list[i]->name, name)) { return gbinder_rpc_protocol_list[i]; } } return NULL; } static void gbinder_rpc_protocol_map_add_default( GHashTable* map, const char* dev, const GBinderRpcProtocol* protocol) { if (!g_hash_table_contains(map, dev)) { g_hash_table_insert(map, g_strdup(dev), (gpointer) protocol); } } static gconstpointer gbinder_rpc_protocol_value_map( const char* name) { return gbinder_rpc_protocol_find(name); } static GHashTable* gbinder_rpc_protocol_load_config() { GHashTable* map = gbinder_config_load(CONF_GROUP, gbinder_rpc_protocol_value_map); /* Add default configuration if it's not overridden */ gbinder_rpc_protocol_map_add_default(map, GBINDER_DEFAULT_BINDER, &gbinder_rpc_protocol_aidl); gbinder_rpc_protocol_map_add_default(map, GBINDER_DEFAULT_HWBINDER, &gbinder_rpc_protocol_hidl); return map; } /* Runs at exit */ void gbinder_rpc_protocol_exit() { if (gbinder_rpc_protocol_map) { g_hash_table_destroy(gbinder_rpc_protocol_map); gbinder_rpc_protocol_map = NULL; } /* Reset the default too, mostly for unit testing */ gbinder_rpc_protocol_default = &DEFAULT_PROTOCOL; } /*==========================================================================* * Interface *==========================================================================*/ const GBinderRpcProtocol* gbinder_rpc_protocol_for_device( const char* dev) { if (dev) { const GBinderRpcProtocol* protocol; if (!gbinder_rpc_protocol_map) { const GBinderRpcProtocol* p; /* One-time initialization */ gbinder_rpc_protocol_map = gbinder_rpc_protocol_load_config(); /* "Default" is a special value stored in a special variable */ p = g_hash_table_lookup(gbinder_rpc_protocol_map, CONF_DEFAULT); if (p) { g_hash_table_remove(gbinder_rpc_protocol_map, CONF_DEFAULT); gbinder_rpc_protocol_default = p; } else { gbinder_rpc_protocol_default = &DEFAULT_PROTOCOL; } } protocol = g_hash_table_lookup(gbinder_rpc_protocol_map, dev); if (protocol) { GDEBUG("Using %s protocol for %s", protocol->name, dev); return protocol; } GDEBUG("Using default protocol %s for %s", gbinder_rpc_protocol_default->name, dev); } else { GDEBUG("Using default protocol %s", gbinder_rpc_protocol_default->name); } return gbinder_rpc_protocol_default; } const GBinderRpcProtocol* gbinder_rpc_protocol_by_name( const char* protocol_name) { return gbinder_rpc_protocol_find(protocol_name); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_rpc_protocol.h000066400000000000000000000065551526127616700210720ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2025 Jolla Mobile Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_RPC_PROTOCOL_H #define GBINDER_RPC_PROTOCOL_H #include "gbinder_types_p.h" /* * There are several versions of binder RPC protocol with diffferent * transaction headers and transaction codes. */ struct gbinder_rpc_protocol { const char* name; guint32 ping_tx; void (*write_ping)(GBinderWriter* writer); void (*write_rpc_header)(GBinderWriter* writer, const char* iface); const char* (*read_rpc_header)(GBinderReader* reader, guint32 txcode, char** iface); /* * For the sake of simplicity, let's assume that the trailer has a * fixed size and that size is the same on both 32 and 64 bit platforms. * Also note that finish_unflatten_binder() is only invoked for the * remote objects that are not NULL, otherwise flat_binder_object_extra * bytes are just skipped. */ gsize flat_binder_object_extra; void (*finish_flatten_binder)(void* out, GBINDER_STABILITY_LEVEL stability); void (*finish_unflatten_binder)(const void* in, GBinderRemoteObject* obj); void (*write_fmq_descriptor)(GBinderWriter* writer, const GBinderFmq* queue); }; const GBinderRpcProtocol* gbinder_rpc_protocol_by_name( const char* protocol_name) GBINDER_INTERNAL; /* Returns one of the above based on the device name */ const GBinderRpcProtocol* gbinder_rpc_protocol_for_device( const char* dev) GBINDER_INTERNAL; /* Runs at exit, declared here strictly for unit tests */ void gbinder_rpc_protocol_exit( void) GBINDER_DESTRUCTOR GBINDER_INTERNAL; #endif /* GBINDER_RPC_PROTOCOL_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager.c000066400000000000000000001064171526127616700213510ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_p.h" #include "gbinder_client_p.h" #include "gbinder_config.h" #include "gbinder_local_object_p.h" #include "gbinder_remote_object_p.h" #include "gbinder_eventloop_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_log.h" #include #include #include /*==========================================================================* * * Different versions of Android come with different flavors of service * managers. They are usually based on these two more or less independent * variants: * * platform/frameworks/native/cmds/servicemanager/ServiceManager.cpp * platform/system/hwservicemanager/ServiceManager.cpp * * They are talking slightly different protocols which slightly mutate * from version to version. If that's not complex enough, different * kinds of service managers can be running simultaneously, serving * different binder devices. Specific device => servicemanager mapping * can be optionally configured in /etc/gbinder.conf file. The default * service manager configuration looks like this: * * [ServiceManager] * Default = aidl * /dev/binder = aidl * /dev/hwbinder = hidl * *==========================================================================*/ #define CONF_GROUP GBINDER_CONFIG_GROUP_SERVICEMANAGER #define CONF_DEFAULT GBINDER_CONFIG_VALUE_DEFAULT typedef struct gbinder_servicemanager_type { const char* name; GType (*get_type)(void); } GBinderServiceManagerType; static const GBinderServiceManagerType gbinder_servicemanager_types[] = { { "aidl", gbinder_servicemanager_aidl_get_type }, { "aidl2", gbinder_servicemanager_aidl2_get_type }, { "aidl3", gbinder_servicemanager_aidl3_get_type }, { "aidl4", gbinder_servicemanager_aidl3_get_type }, { "aidl5", gbinder_servicemanager_aidl5_get_type }, { "aidl6", gbinder_servicemanager_aidl6_get_type }, { "hidl", gbinder_servicemanager_hidl_get_type } }; #define SERVICEMANAGER_TYPE_AIDL (gbinder_servicemanager_types + 0) #define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 6) #define SERVICEMANAGER_TYPE_DEFAULT SERVICEMANAGER_TYPE_AIDL static GHashTable* gbinder_servicemanager_map = NULL; static const GBinderServiceManagerType* gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT; #define PRESENSE_WAIT_MS_MIN (100) #define PRESENSE_WAIT_MS_MAX (1000) #define PRESENSE_WAIT_MS_STEP (100) typedef struct gbinder_servicemanager_watch { char* name; char* detail; GQuark quark; gboolean watched; } GBinderServiceManagerWatch; struct gbinder_servicemanager_priv { GHashTable* watch_table; gulong death_id; gboolean present; GBinderEventLoopTimeout* presence_check; guint presence_check_delay_ms; GBinderEventLoopCallback* autorelease_cb; GSList* autorelease; }; G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager, G_TYPE_OBJECT) #define PARENT_CLASS gbinder_servicemanager_parent_class #define GBINDER_SERVICEMANAGER(obj) \ G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER, \ GBinderServiceManager) #define GBINDER_SERVICEMANAGER_GET_CLASS(obj) \ G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER, \ GBinderServiceManagerClass) #define GBINDER_IS_SERVICEMANAGER_TYPE(klass) \ G_TYPE_CHECK_CLASS_TYPE(klass, GBINDER_TYPE_SERVICEMANAGER) enum gbinder_servicemanager_signal { SIGNAL_PRESENCE, SIGNAL_REGISTRATION, SIGNAL_COUNT }; static const char SIGNAL_PRESENCE_NAME[] = "servicemanager-presence"; static const char SIGNAL_REGISTRATION_NAME[] = "servicemanager-registration"; #define DETAIL_LEN 32 static guint gbinder_servicemanager_signals[SIGNAL_COUNT] = { 0 }; /*==========================================================================* * Implementation *==========================================================================*/ static GBinderServiceManagerClass* gbinder_servicemanager_class_ref( GType type) { if (G_LIKELY(type)) { GTypeClass* klass = g_type_class_ref(type); if (klass) { if (GBINDER_IS_SERVICEMANAGER_TYPE(klass)) { return GBINDER_SERVICEMANAGER_CLASS(klass); } g_type_class_unref(klass); } } return NULL; } static GBinderServiceManagerWatch* gbinder_servicemanager_watch_new( const char* name) { GBinderServiceManagerWatch* watch = g_new0(GBinderServiceManagerWatch, 1); watch->name = g_strdup(name); watch->detail = g_compute_checksum_for_string(G_CHECKSUM_MD5, name, -1); watch->quark = g_quark_from_string(watch->detail); return watch; } static void gbinder_servicemanager_watch_free( gpointer data) { GBinderServiceManagerWatch* watch = data; g_free(watch->name); g_free(watch->detail); g_free(watch); } typedef struct gbinder_servicemanager_list_tx_data { GBinderServiceManager* sm; GBinderServiceManagerListFunc func; char** result; void* user_data; } GBinderServiceManagerListTxData; static void gbinder_servicemanager_list_tx_exec( const GBinderIpcTx* tx) { GBinderServiceManagerListTxData* data = tx->user_data; data->result = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)-> list(data->sm, &gbinder_ipc_sync_worker); } static void gbinder_servicemanager_list_tx_done( const GBinderIpcTx* tx) { GBinderServiceManagerListTxData* data = tx->user_data; if (!data->func(data->sm, data->result, data->user_data)) { g_strfreev(data->result); } data->result = NULL; } static void gbinder_servicemanager_list_tx_free( gpointer user_data) { GBinderServiceManagerListTxData* data = user_data; g_strfreev(data->result); gbinder_servicemanager_unref(data->sm); g_slice_free(GBinderServiceManagerListTxData, data); } typedef struct gbinder_servicemanager_get_service_tx { GBinderServiceManager* sm; GBinderServiceManagerGetServiceFunc func; GBinderRemoteObject* obj; int status; char* name; void* user_data; } GBinderServiceManagerGetServiceTxData; static void gbinder_servicemanager_get_service_tx_exec( const GBinderIpcTx* tx) { GBinderServiceManagerGetServiceTxData* data = tx->user_data; data->obj = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)-> get_service(data->sm, data->name, &data->status, &gbinder_ipc_sync_worker); } static void gbinder_servicemanager_get_service_tx_done( const GBinderIpcTx* tx) { GBinderServiceManagerGetServiceTxData* data = tx->user_data; data->func(data->sm, data->obj, data->status, data->user_data); } static void gbinder_servicemanager_get_service_tx_free( gpointer user_data) { GBinderServiceManagerGetServiceTxData* data = user_data; gbinder_servicemanager_unref(data->sm); gbinder_remote_object_unref(data->obj); g_free(data->name); g_slice_free(GBinderServiceManagerGetServiceTxData, data); } typedef struct gbinder_servicemanager_add_service_tx { GBinderServiceManager* sm; GBinderServiceManagerAddServiceFunc func; GBinderLocalObject* obj; int status; char* name; void* user_data; } GBinderServiceManagerAddServiceTxData; static void gbinder_servicemanager_add_service_tx_exec( const GBinderIpcTx* tx) { GBinderServiceManagerAddServiceTxData* data = tx->user_data; data->status = GBINDER_SERVICEMANAGER_GET_CLASS(data->sm)-> add_service(data->sm, data->name, data->obj, &gbinder_ipc_sync_worker); } static void gbinder_servicemanager_add_service_tx_done( const GBinderIpcTx* tx) { GBinderServiceManagerAddServiceTxData* data = tx->user_data; data->func(data->sm, data->status, data->user_data); } static void gbinder_servicemanager_add_service_tx_free( gpointer user_data) { GBinderServiceManagerAddServiceTxData* data = user_data; gbinder_servicemanager_unref(data->sm); gbinder_local_object_unref(data->obj); g_free(data->name); g_slice_free(GBinderServiceManagerAddServiceTxData, data); } static void gbinder_servicemanager_reanimated( GBinderServiceManager* self) { GBinderServiceManagerPriv* priv = self->priv; if (priv->presence_check) { gbinder_timeout_remove(priv->presence_check); priv->presence_check = NULL; } GINFO("Service manager %s has appeared", self->dev); /* Re-arm the watches */ if (g_hash_table_size(priv->watch_table) > 0) { gpointer value; GHashTableIter it; GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); g_hash_table_iter_init(&it, priv->watch_table); while (g_hash_table_iter_next(&it, NULL, &value)) { GBinderServiceManagerWatch* watch = value; GASSERT(!watch->watched); watch->watched = klass->watch(self, watch->name); if (watch->watched) { GDEBUG("Watching %s", watch->name); } else { GWARN("Failed to watch %s", watch->name); } } } g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_PRESENCE], 0); } static gboolean gbinder_servicemanager_presense_check_timer( gpointer user_data) { GBinderServiceManager* self = GBINDER_SERVICEMANAGER(user_data); GBinderRemoteObject* remote = self->client->remote; GBinderServiceManagerPriv* priv = self->priv; gboolean result; GASSERT(remote->dead); gbinder_servicemanager_ref(self); if (gbinder_remote_object_reanimate(remote)) { /* Done */ priv->presence_check = NULL; gbinder_servicemanager_reanimated(self); result = G_SOURCE_REMOVE; } else if (priv->presence_check_delay_ms < PRESENSE_WAIT_MS_MAX) { priv->presence_check_delay_ms += PRESENSE_WAIT_MS_STEP; priv->presence_check = gbinder_timeout_add(priv->presence_check_delay_ms, gbinder_servicemanager_presense_check_timer, self); result = G_SOURCE_REMOVE; } else { result = G_SOURCE_CONTINUE; } gbinder_servicemanager_unref(self); return result; } static void gbinder_servicemanager_presence_check_start( GBinderServiceManager* self) { GBinderServiceManagerPriv* priv = self->priv; GASSERT(!priv->presence_check); priv->presence_check_delay_ms = PRESENSE_WAIT_MS_MIN; priv->presence_check = gbinder_timeout_add(PRESENSE_WAIT_MS_MIN, gbinder_servicemanager_presense_check_timer, self); } static void gbinder_servicemanager_died( GBinderRemoteObject* remote, void* user_data) { GBinderServiceManager* self = GBINDER_SERVICEMANAGER(user_data); GBinderServiceManagerPriv* priv = self->priv; GWARN("Service manager %s has died", self->dev); gbinder_servicemanager_presence_check_start(self); /* Will re-arm watches after servicemanager gets restarted */ if (g_hash_table_size(priv->watch_table) > 0) { gpointer value; GHashTableIter it; GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); g_hash_table_iter_init(&it, priv->watch_table); while (g_hash_table_iter_next(&it, NULL, &value)) { GBinderServiceManagerWatch* watch = value; if (watch->watched) { GDEBUG("Unwatching %s", watch->name); watch->watched = FALSE; klass->unwatch(self, watch->name); } } } g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_PRESENCE], 0); } static void gbinder_servicemanager_sleep_ms( gulong ms) { struct timespec wait; wait.tv_sec = ms/1000; /* seconds */ wait.tv_nsec = (ms % 1000) * 1000000; /* nanoseconds */ while (nanosleep(&wait, &wait) == -1 && errno == EINTR && (wait.tv_sec > 0 || wait.tv_nsec > 0)); } static void gbinder_servicemanager_autorelease_cb( gpointer data) { GBinderServiceManager* self = GBINDER_SERVICEMANAGER(data); GBinderServiceManagerPriv* priv = self->priv; GSList* list = priv->autorelease; priv->autorelease_cb = NULL; priv->autorelease = NULL; g_slist_free_full(list, g_object_unref); } static void gbinder_servicemanager_map_add_default( GHashTable* map, const char* dev, const GBinderServiceManagerType* type) { if (!g_hash_table_contains(map, dev)) { g_hash_table_insert(map, g_strdup(dev), (gpointer) type); } } static gconstpointer gbinder_servicemanager_value_map( const char* name) { guint i; for (i = 0; i < G_N_ELEMENTS(gbinder_servicemanager_types); i++) { const GBinderServiceManagerType* t = gbinder_servicemanager_types + i; if (!g_strcmp0(name, t->name)) { return t; } } return NULL; } static GHashTable* gbinder_servicemanager_load_config() { GHashTable* map = gbinder_config_load(CONF_GROUP, gbinder_servicemanager_value_map); /* Add default configuration if it's not overridden */ gbinder_servicemanager_map_add_default(map, GBINDER_DEFAULT_BINDER, SERVICEMANAGER_TYPE_AIDL); gbinder_servicemanager_map_add_default(map, GBINDER_DEFAULT_HWBINDER, SERVICEMANAGER_TYPE_HIDL); return map; } /* Runs at exit */ void gbinder_servicemanager_exit( void) { if (gbinder_servicemanager_map) { g_hash_table_destroy(gbinder_servicemanager_map); gbinder_servicemanager_map = NULL; } /* Reset the default too, mostly for unit testing */ gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT; } /*==========================================================================* * Internal interface *==========================================================================*/ GBinderServiceManager* gbinder_servicemanager_new_with_type( GType type, const char* dev, const char* rpc_protocol) { GBinderServiceManager* self = NULL; GBinderServiceManagerClass* klass = gbinder_servicemanager_class_ref(type); if (klass) { GBinderIpc* ipc; if (!dev) dev = klass->default_device; ipc = gbinder_ipc_new(dev, rpc_protocol); if (ipc) { /* Create a (possibly) dead service manager object */ GBinderRemoteObject* object = gbinder_ipc_get_service_manager(ipc); if (object) { gboolean first_ref; /* Lock */ g_mutex_lock(&klass->mutex); if (klass->table) { self = g_hash_table_lookup(klass->table, dev); } if (self) { first_ref = FALSE; gbinder_servicemanager_ref(self); } else { char* key = g_strdup(dev); /* Owned by the hashtable */ first_ref = TRUE; self = g_object_new(type, NULL); self->client = gbinder_client_new(object, klass->iface); self->dev = gbinder_remote_object_dev(object); if (!klass->table) { klass->table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } g_hash_table_replace(klass->table, key, self); } g_mutex_unlock(&klass->mutex); /* Unlock */ if (first_ref) { GBinderServiceManagerPriv* priv = self->priv; priv->death_id = gbinder_remote_object_add_death_handler(object, gbinder_servicemanager_died, self); /* Query the actual state if necessary */ gbinder_remote_object_reanimate(object); if (object->dead) { gbinder_servicemanager_presence_check_start(self); } GDEBUG("%s has %sservice manager", dev, object->dead ? "no " : ""); } gbinder_remote_object_unref(object); } gbinder_ipc_unref(ipc); } g_type_class_unref(klass); } return self; } void gbinder_servicemanager_service_registered( GBinderServiceManager* self, const char* name) { GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); GBinderServiceManagerPriv* priv = self->priv; GBinderServiceManagerWatch* watch = NULL; const char* normalized_name; char* tmp_name = NULL; switch (klass->check_name(self, name)) { case GBINDER_SERVICEMANAGER_NAME_OK: normalized_name = name; break; case GBINDER_SERVICEMANAGER_NAME_NORMALIZE: normalized_name = tmp_name = klass->normalize_name(self, name); break; default: normalized_name = NULL; break; } if (normalized_name) { watch = g_hash_table_lookup(priv->watch_table, normalized_name); } g_free(tmp_name); g_signal_emit(self, gbinder_servicemanager_signals[SIGNAL_REGISTRATION], watch ? watch->quark : 0, name); } /*==========================================================================* * Interface *==========================================================================*/ GBinderServiceManager* gbinder_servicemanager_new( const char* dev) { return gbinder_servicemanager_new2(dev, NULL, NULL); } GBinderServiceManager* gbinder_servicemanager_new2( const char* dev, const char* sm_protocol, const char* rpc_protocol) /* Since 1.1.20 */ { if (!dev) { return NULL; } else if (!sm_protocol) { const GBinderServiceManagerType* type; /* One-time initialization */ if (!gbinder_servicemanager_map) { gbinder_servicemanager_map = gbinder_servicemanager_load_config(); /* "Default" is a special value stored in a special variable */ type = g_hash_table_lookup(gbinder_servicemanager_map, CONF_DEFAULT); if (type) { g_hash_table_remove(gbinder_servicemanager_map, CONF_DEFAULT); gbinder_servicemanager_default = type; } else { gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT; } } /* If no protocol is specified, pick one up based on the device name */ type = g_hash_table_lookup(gbinder_servicemanager_map, dev); if (type) { GDEBUG("Using %s service manager for %s", type->name, dev); } else { type = gbinder_servicemanager_default; GDEBUG("Using default service manager %s for %s", type->name, dev); } return gbinder_servicemanager_new_with_type(type->get_type(), dev, rpc_protocol); } else { /* If protocol name is specified, it must be a valid one */ const GBinderServiceManagerType* type = gbinder_servicemanager_value_map(sm_protocol); if (type) { return gbinder_servicemanager_new_with_type(type->get_type(), dev, rpc_protocol); } else { GWARN("Unknown servicemanager protocol %s", sm_protocol); return NULL; } } } GBinderLocalObject* gbinder_servicemanager_new_local_object( GBinderServiceManager* self, const char* iface, GBinderLocalTransactFunc txproc, void* user_data) { const char* ifaces[2]; ifaces[0] = iface; ifaces[1] = NULL; return gbinder_servicemanager_new_local_object2 (self, ifaces, txproc, user_data); } GBinderLocalObject* gbinder_servicemanager_new_local_object2( GBinderServiceManager* self, const char* const* ifaces, GBinderLocalTransactFunc txproc, void* user_data) { if (G_LIKELY(self)) { return gbinder_local_object_new(gbinder_client_ipc(self->client), ifaces, txproc, user_data); } return NULL; } GBinderServiceManager* gbinder_servicemanager_ref( GBinderServiceManager* self) { if (G_LIKELY(self)) { g_object_ref(GBINDER_SERVICEMANAGER(self)); } return self; } void gbinder_servicemanager_unref( GBinderServiceManager* self) { if (G_LIKELY(self)) { g_object_unref(GBINDER_SERVICEMANAGER(self)); } } const char* gbinder_servicemanager_device( GBinderServiceManager* self) /* Since 1.1.14 */ { return G_LIKELY(self) ? self->dev : NULL; } gboolean gbinder_servicemanager_is_present( GBinderServiceManager* self) /* Since 1.0.25 */ { return G_LIKELY(self) && !self->client->remote->dead; } gboolean gbinder_servicemanager_wait( GBinderServiceManager* self, long max_wait_ms) /* Since 1.0.25 */ { if (G_LIKELY(self)) { GBinderRemoteObject* remote = self->client->remote; if (!remote->dead) { return TRUE; } else if (gbinder_remote_object_reanimate(remote)) { gbinder_servicemanager_reanimated(self); return TRUE; } else if (max_wait_ms != 0) { /* Zero timeout means a singe check and it's already done */ long delay_ms = PRESENSE_WAIT_MS_MIN; while (max_wait_ms != 0) { if (max_wait_ms > 0) { if (max_wait_ms < delay_ms) { delay_ms = max_wait_ms; max_wait_ms = 0; } else { max_wait_ms -= delay_ms; } } gbinder_servicemanager_sleep_ms(delay_ms); if (gbinder_remote_object_reanimate(remote)) { gbinder_servicemanager_reanimated(self); return TRUE; } if (delay_ms < PRESENSE_WAIT_MS_MAX) { delay_ms += PRESENSE_WAIT_MS_STEP; if (delay_ms > PRESENSE_WAIT_MS_MAX) { delay_ms = PRESENSE_WAIT_MS_MAX; } } } /* Timeout */ GWARN("Timeout waiting for service manager %s", self->dev); } } return FALSE; } gulong gbinder_servicemanager_list( GBinderServiceManager* self, GBinderServiceManagerListFunc func, void* user_data) { if (G_LIKELY(self) && func) { GBinderServiceManagerListTxData* data = g_slice_new0(GBinderServiceManagerListTxData); data->sm = gbinder_servicemanager_ref(self); data->func = func; data->user_data = user_data; return gbinder_ipc_transact_custom(gbinder_client_ipc(self->client), gbinder_servicemanager_list_tx_exec, gbinder_servicemanager_list_tx_done, gbinder_servicemanager_list_tx_free, data); } return 0; } char** gbinder_servicemanager_list_sync( GBinderServiceManager* self) { if (G_LIKELY(self)) { return GBINDER_SERVICEMANAGER_GET_CLASS(self)-> list(self, &gbinder_ipc_sync_main); } return NULL; } gulong gbinder_servicemanager_get_service( GBinderServiceManager* self, const char* name, GBinderServiceManagerGetServiceFunc func, void* user_data) { if (G_LIKELY(self) && func && name) { GBinderServiceManagerGetServiceTxData* data = g_slice_new0(GBinderServiceManagerGetServiceTxData); data->sm = gbinder_servicemanager_ref(self); data->func = func; data->name = g_strdup(name); data->user_data = user_data; data->status = (-EFAULT); return gbinder_ipc_transact_custom(gbinder_client_ipc(self->client), gbinder_servicemanager_get_service_tx_exec, gbinder_servicemanager_get_service_tx_done, gbinder_servicemanager_get_service_tx_free, data); } return 0; } GBinderRemoteObject* /* autoreleased */ gbinder_servicemanager_get_service_sync( GBinderServiceManager* self, const char* name, int* status) { GBinderRemoteObject* obj = NULL; if (G_LIKELY(self) && name) { obj = GBINDER_SERVICEMANAGER_GET_CLASS(self)-> get_service(self, name, status, &gbinder_ipc_sync_main); if (obj) { GBinderServiceManagerPriv* priv = self->priv; priv->autorelease = g_slist_prepend(priv->autorelease, obj); if (!priv->autorelease_cb) { priv->autorelease_cb = gbinder_idle_callback_schedule_new (gbinder_servicemanager_autorelease_cb, self, NULL); } } } else if (status) { *status = (-EINVAL); } return obj; } gulong gbinder_servicemanager_add_service( GBinderServiceManager* self, const char* name, GBinderLocalObject* obj, GBinderServiceManagerAddServiceFunc func, void* user_data) { if (G_LIKELY(self) && func && name) { GBinderServiceManagerAddServiceTxData* data = g_slice_new0(GBinderServiceManagerAddServiceTxData); data->sm = gbinder_servicemanager_ref(self); data->obj = gbinder_local_object_ref(obj); data->func = func; data->name = g_strdup(name); data->user_data = user_data; data->status = (-EFAULT); return gbinder_ipc_transact_custom(gbinder_client_ipc(self->client), gbinder_servicemanager_add_service_tx_exec, gbinder_servicemanager_add_service_tx_done, gbinder_servicemanager_add_service_tx_free, data); } return 0; } int gbinder_servicemanager_add_service_sync( GBinderServiceManager* self, const char* name, GBinderLocalObject* obj) { if (G_LIKELY(self) && name && obj) { return GBINDER_SERVICEMANAGER_GET_CLASS(self)-> add_service(self, name, obj, &gbinder_ipc_sync_main); } else { return (-EINVAL); } } void gbinder_servicemanager_cancel( GBinderServiceManager* self, gulong id) { if (G_LIKELY(self)) { gbinder_ipc_cancel(gbinder_client_ipc(self->client), id); } } gulong gbinder_servicemanager_add_presence_handler( GBinderServiceManager* self, GBinderServiceManagerFunc func, void* user_data) /* Since 1.0.25 */ { return (G_LIKELY(self) && G_LIKELY(func)) ? g_signal_connect(self, SIGNAL_PRESENCE_NAME, G_CALLBACK(func), user_data) : 0; } gulong gbinder_servicemanager_add_registration_handler( GBinderServiceManager* self, const char* name, GBinderServiceManagerRegistrationFunc func, void* data) /* Since 1.0.13 */ { gulong id = 0; if (G_LIKELY(self) && G_LIKELY(func)) { char* tmp_name = NULL; GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); switch (klass->check_name(self, name)) { case GBINDER_SERVICEMANAGER_NAME_OK: break; case GBINDER_SERVICEMANAGER_NAME_NORMALIZE: name = tmp_name = klass->normalize_name(self, name); break; default: name = NULL; break; } if (name) { GBinderServiceManagerPriv* priv = self->priv; GBinderServiceManagerWatch* watch = NULL; watch = g_hash_table_lookup(priv->watch_table, name); if (!watch) { watch = gbinder_servicemanager_watch_new(name); g_hash_table_insert(priv->watch_table, watch->name, watch); } if (!watch->watched && !self->client->remote->dead) { watch->watched = klass->watch(self, watch->name); if (watch->watched) { GDEBUG("Watching %s", watch->name); } else { GWARN("Failed to watch %s", watch->name); } } id = g_signal_connect_closure_by_id(self, gbinder_servicemanager_signals[SIGNAL_REGISTRATION], watch->quark, g_cclosure_new(G_CALLBACK(func), data, NULL), FALSE); } g_free(tmp_name); } return id; } void gbinder_servicemanager_remove_handler( GBinderServiceManager* self, gulong id) /* Since 1.0.13 */ { gbinder_servicemanager_remove_handlers(self, &id, 1); } void gbinder_servicemanager_remove_handlers( GBinderServiceManager* self, gulong* ids, guint count) /* Since 1.0.25 */ { if (G_LIKELY(self) && G_LIKELY(ids) && G_LIKELY(count)) { GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); GBinderServiceManagerPriv* priv = self->priv; GHashTableIter it; gpointer value; gutil_disconnect_handlers(self, ids, count); /* * Regardless of whether we have actually removed any handlers, still * check all watchers because there's a possibility that some handlers * got removed with a straight g_signal_handler_disconnect() call. */ g_hash_table_iter_init(&it, priv->watch_table); while (g_hash_table_iter_next(&it, NULL, &value)) { GBinderServiceManagerWatch* watch = value; if (!g_signal_has_handler_pending(self, gbinder_servicemanager_signals[SIGNAL_REGISTRATION], watch->quark, TRUE)) { /* No need to keep the watch around if no one is listening */ GDEBUG("Dropping watch %s", watch->name); if (watch->watched) { watch->watched = FALSE; klass->unwatch(self, watch->name); } g_hash_table_iter_remove(&it); } } } } /* * These two exist mostly for backward compatibility. Normally, * gbinder_servicemanager_new() should be used, to allow the type of * service manager to be configurable per device via /etc/gbinder.conf */ GBinderServiceManager* gbinder_defaultservicemanager_new( const char* dev) { return gbinder_servicemanager_new_with_type (gbinder_servicemanager_aidl_get_type(), dev, NULL); } GBinderServiceManager* gbinder_hwservicemanager_new( const char* dev) { return gbinder_servicemanager_new_with_type (gbinder_servicemanager_hidl_get_type(), dev, NULL); } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_servicemanager_init( GBinderServiceManager* self) { GBinderServiceManagerPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GBINDER_TYPE_SERVICEMANAGER, GBinderServiceManagerPriv); self->priv = priv; priv->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, gbinder_servicemanager_watch_free); } static void gbinder_servicemanager_dispose( GObject* object) { GBinderServiceManager* self = GBINDER_SERVICEMANAGER(object); GBinderServiceManagerClass* klass = GBINDER_SERVICEMANAGER_GET_CLASS(self); GVERBOSE_("%s", self->dev); /* Lock */ g_mutex_lock(&klass->mutex); /* * The follow can happen: * * 1. Last reference goes away. * 2. gbinder_servicemanager_dispose() is invoked by glib * 3. Before gbinder_servicemanager_dispose() grabs the * lock, gbinder_servicemanager_new() gets there first, * finds the object in the hashtable, bumps its refcount * (under the lock) and returns the reference to the caller. * 4. gbinder_servicemanager_dispose() gets its lock, finds * that the object's refcount is greater than zero and leaves * the object in the table. * * It's OK for a GObject to get re-referenced in dispose. * glib will recheck the refcount once dispose returns, * gbinder_servicemanager_finalize() will not be called * this time around. */ if (klass->table && g_atomic_int_get(&object->ref_count) <= 1) { g_hash_table_remove(klass->table, self->dev); if (g_hash_table_size(klass->table) == 0) { g_hash_table_unref(klass->table); klass->table = NULL; } } g_mutex_unlock(&klass->mutex); /* Unlock */ G_OBJECT_CLASS(PARENT_CLASS)->dispose(object); } static void gbinder_servicemanager_finalize( GObject* object) { GBinderServiceManager* self = GBINDER_SERVICEMANAGER(object); GBinderServiceManagerPriv* priv = self->priv; gbinder_timeout_remove(priv->presence_check); gbinder_remote_object_remove_handler(self->client->remote, priv->death_id); gbinder_idle_callback_destroy(priv->autorelease_cb); g_slist_free_full(priv->autorelease, g_object_unref); g_hash_table_destroy(priv->watch_table); gbinder_client_unref(self->client); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } void gbinder_servicemanager_class_common_init( GBinderServiceManagerClass* klass) { /* * Every subclass must call this function in its class init routine, * else they may end up sharing the table. Even though the base class * is abstract and can't be directly instantiated (and therefore its * table remains null), AIDL classes form a hierarchy and may actually * end up sharing it. * * It's also a good idea to individually init the mutex for every * subclass, instead of assuming that memcpy would do the right thing. */ g_mutex_init(&klass->mutex); klass->table = NULL; } static void gbinder_servicemanager_class_init( GBinderServiceManagerClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); GType type = G_OBJECT_CLASS_TYPE(klass); gbinder_servicemanager_class_common_init(klass); g_type_class_add_private(klass, sizeof(GBinderServiceManagerPriv)); object_class->dispose = gbinder_servicemanager_dispose; object_class->finalize = gbinder_servicemanager_finalize; gbinder_servicemanager_signals[SIGNAL_PRESENCE] = g_signal_new(SIGNAL_PRESENCE_NAME, type, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); gbinder_servicemanager_signals[SIGNAL_REGISTRATION] = g_signal_new(SIGNAL_REGISTRATION_NAME, type, G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl.c000066400000000000000000000457451526127616700223500ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_aidl.h" #include "gbinder_servicepoll.h" #include "gbinder_eventloop_p.h" #include "gbinder_client_p.h" #include "gbinder_log.h" #include #include #include #include typedef struct gbinder_servicemanager_aidl_poll { GBinderServicePoll* poll; char* name; gulong handler_id; GBinderEventLoopTimeout* notify; } GBinderServiceManagerAidlPoll; typedef struct gbinder_servicemanager_aidl_callback_registration_call { int ref_count; /* tx and registration_call_table hold the refs */ GBinderServiceManagerAidl* obj; gulong tx; char* name; } GBinderServiceManagerAidlCallbackRegistrationCall; struct gbinder_servicemanager_aidl_priv { GBinderServicePoll* poll; GBinderLocalObject* callback; GHashTable* registration_call_table; GHashTable* poll_table; }; G_DEFINE_TYPE(GBinderServiceManagerAidl, gbinder_servicemanager_aidl, GBINDER_TYPE_SERVICEMANAGER) #define PARENT_CLASS gbinder_servicemanager_aidl_parent_class #define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, \ GBINDER_TYPE_SERVICEMANAGER_AIDL, GBinderServiceManagerAidl) #define GET_THIS_CLASS(obj) GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) #define SERVICEMANAGER_AIDL_IFACE "android.os.IServiceManager" #define SERVICEMANAGER_AIDL_CALLBACK_IFACE "android.os.IServiceCallback" enum gbinder_servicemanager_aidl_calls { GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION, REGISTER_FOR_NOTIFICATIONS_TRANSACTION, UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION }; /*==========================================================================* * Poll (if registerForNotifications is not supported) *==========================================================================*/ static void gbinder_servicemanager_aidl_poll_proc( GBinderServicePoll* poll, const char* name_added, void* user_data) { GBinderServiceManagerAidlPoll* watch = user_data; if (!g_strcmp0(name_added, watch->name)) { GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll); if (watch->notify) { gbinder_timeout_remove(watch->notify); watch->notify = NULL; } gbinder_servicemanager_service_registered(manager, name_added); } } static gboolean gbinder_servicemanager_aidl_poll_notify( gpointer user_data) { GBinderServiceManagerAidlPoll* watch = user_data; GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll); char* name = g_strdup(watch->name); GASSERT(watch->notify); watch->notify = NULL; gbinder_servicemanager_service_registered(manager, name); g_free(name); return G_SOURCE_REMOVE; } static void gbinder_servicemanager_aidl_poll_free( gpointer user_data) { GBinderServiceManagerAidlPoll* watch = user_data; gbinder_timeout_remove(watch->notify); gbinder_servicepoll_remove_handler(watch->poll, watch->handler_id); gbinder_servicepoll_unref(watch->poll); g_free(watch->name); g_slice_free(GBinderServiceManagerAidlPoll, watch); } static void gbinder_servicemanager_aidl_poll_start( GBinderServiceManagerAidl* self, const char* name) { GBinderServiceManagerAidlPriv* priv = self->priv; GBinderServiceManagerAidlPoll* watch = g_slice_new0(GBinderServiceManagerAidlPoll); watch->name = g_strdup(name); watch->poll = gbinder_servicepoll_new(&self->manager, &priv->poll); watch->handler_id = gbinder_servicepoll_add_handler(priv->poll, gbinder_servicemanager_aidl_poll_proc, watch); if (!priv->poll_table) { priv->poll_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, gbinder_servicemanager_aidl_poll_free); } g_hash_table_replace(priv->poll_table, watch->name, watch); if (gbinder_servicepoll_is_known_name(watch->poll, name)) { watch->notify = gbinder_idle_add(gbinder_servicemanager_aidl_poll_notify, watch); } } /*==========================================================================* * IServiceCallback *==========================================================================*/ enum gbinder_servicemanager_aidl_callback_transactions { ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION }; static GBinderLocalReply* gbinder_servicemanager_aidl_callback( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { GBinderServiceManagerAidl* self = THIS(user_data); const char* iface = gbinder_remote_request_interface(req); if (!g_strcmp0(iface, SERVICEMANAGER_AIDL_CALLBACK_IFACE) && code == ON_REGISTRATION_TRANSACTION) { GBinderReader reader; char* name; /* * IServiceCallback.aidl: * void onRegistration(@utf8InCpp String name, IBinder binder); */ gbinder_remote_request_init_reader(req, &reader); name = gbinder_reader_read_string16(&reader); GDEBUG("%s %u onRegistration %s", iface, code, name); gbinder_servicemanager_service_registered(&self->manager, name); g_free(name); *status = GBINDER_STATUS_OK; } else { GDEBUG("%s %u", iface, code); *status = GBINDER_STATUS_FAILED; } return NULL; } static void gbinder_servicemanager_aidl_callback_registration_call_done( void* user_data) { GBinderServiceManagerAidlCallbackRegistrationCall* call = user_data; GBinderServiceManagerAidlPriv* priv = call->obj->priv; call->tx = 0; if (priv->registration_call_table) { g_hash_table_remove(priv->registration_call_table, call->name); } } static void gbinder_servicemanager_aidl_callback_register_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { GBinderServiceManagerAidlCallbackRegistrationCall* call = user_data; GBinderServiceManagerAidl* self = call->obj; GBinderServiceManagerAidlPriv* priv = self->priv; gbinder_servicemanager_aidl_callback_registration_call_done(call); /* * If registration fails, drop the callback object and revert * to polling. */ if (status != GBINDER_STATUS_OK) { GWARN("registerForNotifications(%s) tx error %d", call->name, status); gbinder_local_object_drop(priv->callback); priv->callback = NULL; g_hash_table_destroy(priv->registration_call_table); priv->registration_call_table = NULL; /* Revert to polling */ gbinder_servicemanager_aidl_poll_start(self, call->name); } } static void gbinder_servicemanager_aidl_callback_unregister_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* call) { gbinder_servicemanager_aidl_callback_registration_call_done(call); } static void gbinder_servicemanager_aidl_callback_registration_call_unref( GBinderServiceManagerAidlCallbackRegistrationCall* call) { call->ref_count--; if (!call->ref_count) { g_object_unref(call->obj); g_free(call->name); g_free(call); } } static void gbinder_servicemanager_aidl_callback_registration_call_destroy( void* user_data) { GBinderServiceManagerAidlCallbackRegistrationCall* call = user_data; /* Transaction completion callback */ gbinder_servicemanager_aidl_callback_registration_call_done(call); gbinder_servicemanager_aidl_callback_registration_call_unref(call); } static void gbinder_servicemanager_aidl_callback_registration_call_drop( void* user_data) { GBinderServiceManagerAidlCallbackRegistrationCall* call = user_data; /* Handles removal from registration_call_table */ if (call->tx) { /* The call is being removed from registration_call_table before * completion. Cancel it. */ gbinder_client_cancel(call->obj->manager.client, call->tx); } gbinder_servicemanager_aidl_callback_registration_call_unref(call); } static gboolean gbinder_servicemanager_aidl_callback_registration_call_submit( GBinderServiceManagerAidl* self, guint code, const char* name, GBinderClientReplyFunc fn) { GBinderServiceManager* manager = &self->manager; GBinderServiceManagerAidlPriv* priv = self->priv; GBinderLocalRequest* req = gbinder_client_new_request(manager->client); GBinderServiceManagerAidlCallbackRegistrationCall* call = g_new0(GBinderServiceManagerAidlCallbackRegistrationCall, 1); call->ref_count = 1; /* Reference for the transaction */ g_object_ref(call->obj = self); call->name = g_strdup(name); /* * Both calls have the same arguments: * * void registerForNotifications(@utf8InCpp String name, * IServiceCallback callback); * * void unregisterForNotifications(@utf8InCpp String name, * IServiceCallback callback); */ gbinder_local_request_append_string16(req, name); gbinder_local_request_append_local_object(req, priv->callback); call->tx = gbinder_client_transact(manager->client, code, 0, req, fn, gbinder_servicemanager_aidl_callback_registration_call_destroy, call); gbinder_local_request_unref(req); if (call->tx) { if (!priv->registration_call_table) { priv->registration_call_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, gbinder_servicemanager_aidl_callback_registration_call_drop); } call->ref_count++; /* Reference for registration_call_table */ g_hash_table_replace(priv->registration_call_table, call->name, call); return TRUE; } else { /* Transaction wasn't submitted, drop the ref */ gbinder_servicemanager_aidl_callback_registration_call_unref(call); return FALSE; } } static gboolean gbinder_servicemanager_aidl_callback_register( GBinderServiceManagerAidl* self, const char* name) { GBinderServiceManager* manager = &self->manager; GBinderServiceManagerAidlPriv* priv = self->priv; GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(self); if (!priv->callback) { priv->callback = gbinder_servicemanager_new_local_object(manager, SERVICEMANAGER_AIDL_CALLBACK_IFACE, gbinder_servicemanager_aidl_callback, self); } return gbinder_servicemanager_aidl_callback_registration_call_submit(self, klass->register_for_notifications_transaction, name, gbinder_servicemanager_aidl_callback_register_reply); } static void gbinder_servicemanager_aidl_callback_unregister( GBinderServiceManagerAidl* self, const char* name) { GBinderServiceManagerAidlPriv* priv = self->priv; if (priv->callback) { GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(self); gbinder_servicemanager_aidl_callback_registration_call_submit(self, klass->unregister_for_notifications_transaction, name, gbinder_servicemanager_aidl_callback_unregister_reply); } } /*==========================================================================* * Implementation *==========================================================================*/ static GBinderLocalRequest* gbinder_servicemanager_aidl_list_services_req( GBinderClient* client, gint32 index) { GBinderLocalRequest* req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, index); return req; } static GBinderLocalRequest* gbinder_servicemanager_aidl_add_service_req( GBinderClient* client, const char* name, GBinderLocalObject* obj) { GBinderLocalRequest* req = gbinder_client_new_request(client); gbinder_local_request_append_string16(req, name); gbinder_local_request_append_local_object(req, obj); gbinder_local_request_append_int32(req, 0); return req; } static char** gbinder_servicemanager_aidl_list( GBinderServiceManager* manager, const GBinderIpcSyncApi* api) { GPtrArray* list = g_ptr_array_new(); GBinderClient* client = manager->client; GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(manager); GBinderLocalRequest* req = klass->list_services_req(client, 0); GBinderRemoteReply* reply; while ((reply = gbinder_client_transact_sync_reply2(client, LIST_SERVICES_TRANSACTION, req, NULL, api)) != NULL) { char* service = gbinder_remote_reply_read_string16(reply); gbinder_remote_reply_unref(reply); if (service) { g_ptr_array_add(list, service); gbinder_local_request_unref(req); req = klass->list_services_req(client, list->len); } else { break; } } gbinder_local_request_unref(req); g_ptr_array_add(list, NULL); return (char**)g_ptr_array_free(list, FALSE); } static GBinderRemoteObject* gbinder_servicemanager_aidl_get_service( GBinderServiceManager* self, const char* name, int* status, const GBinderIpcSyncApi* api) { GBinderRemoteObject* obj; GBinderRemoteReply* reply; GBinderLocalRequest* req = gbinder_client_new_request(self->client); GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(self); gbinder_local_request_append_string16(req, name); reply = gbinder_client_transact_sync_reply2(self->client, klass->check_service_transaction, req, status, api); obj = gbinder_remote_reply_read_object(reply); gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); return obj; } static int gbinder_servicemanager_aidl_add_service( GBinderServiceManager* manager, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api) { int status; GBinderClient* client = manager->client; GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(manager); GBinderLocalRequest* req = klass->add_service_req(client, name, obj); GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2(client, klass->add_service_transaction, req, &status, api); gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); return status; } static GBINDER_SERVICEMANAGER_NAME_CHECK gbinder_servicemanager_aidl_check_name( GBinderServiceManager* self, const char* name) { return GBINDER_SERVICEMANAGER_NAME_OK; } static gboolean gbinder_servicemanager_aidl_watch( GBinderServiceManager* manager, const char* name) { GBinderServiceManagerAidl* self = THIS(manager); GBinderServiceManagerAidlPriv* priv = self->priv; if (priv->poll_table || !gbinder_servicemanager_aidl_callback_register(self, name)) { /* registerForNotifications isn't working */ gbinder_servicemanager_aidl_poll_start(self, name); } return TRUE; } static void gbinder_servicemanager_aidl_unwatch( GBinderServiceManager* manager, const char* name) { GBinderServiceManagerAidl* self = THIS(manager); GBinderServiceManagerAidlPriv* priv = self->priv; if (priv->registration_call_table) { g_hash_table_remove(priv->registration_call_table, name); gbinder_servicemanager_aidl_callback_unregister(self, name); } if (priv->poll_table) { g_hash_table_remove(priv->poll_table, name); } } static void gbinder_servicemanager_aidl_init( GBinderServiceManagerAidl* self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GBINDER_TYPE_SERVICEMANAGER_AIDL, GBinderServiceManagerAidlPriv); } static void gbinder_servicemanager_aidl_finalize( GObject* object) { GBinderServiceManagerAidl* self = THIS(object); GBinderServiceManagerAidlPriv* priv = self->priv; gbinder_local_object_drop(priv->callback); if (priv->registration_call_table) { g_hash_table_destroy(priv->registration_call_table); } if (priv->poll_table) { g_hash_table_destroy(priv->poll_table); } G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_servicemanager_aidl_class_init( GBinderServiceManagerAidlClass* klass) { GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass); GObjectClass* object = G_OBJECT_CLASS(klass); gbinder_servicemanager_class_common_init(manager); g_type_class_add_private(klass, sizeof(GBinderServiceManagerAidlPriv)); klass->check_service_transaction = CHECK_SERVICE_TRANSACTION; klass->add_service_transaction = ADD_SERVICE_TRANSACTION; klass->list_services_transaction = LIST_SERVICES_TRANSACTION; klass->register_for_notifications_transaction = REGISTER_FOR_NOTIFICATIONS_TRANSACTION; klass->unregister_for_notifications_transaction = UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION; klass->list_services_req = gbinder_servicemanager_aidl_list_services_req; klass->add_service_req = gbinder_servicemanager_aidl_add_service_req; manager->iface = SERVICEMANAGER_AIDL_IFACE; manager->default_device = GBINDER_DEFAULT_BINDER; manager->list = gbinder_servicemanager_aidl_list; manager->get_service = gbinder_servicemanager_aidl_get_service; manager->add_service = gbinder_servicemanager_aidl_add_service; manager->check_name = gbinder_servicemanager_aidl_check_name; /* normalize_name is not needed */ manager->watch = gbinder_servicemanager_aidl_watch; manager->unwatch = gbinder_servicemanager_aidl_unwatch; object->finalize = gbinder_servicemanager_aidl_finalize; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl.h000066400000000000000000000067671526127616700223560ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2020-2022 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SERVICEMANAGER_AIDL_H #define GBINDER_SERVICEMANAGER_AIDL_H #include "gbinder_servicemanager_p.h" typedef struct gbinder_servicemanager_aidl_priv GBinderServiceManagerAidlPriv; typedef struct gbinder_servicemanager_aidl { GBinderServiceManager manager; GBinderServiceManagerAidlPriv* priv; } GBinderServiceManagerAidl; typedef struct gbinder_servicemanager_aidl_class { GBinderServiceManagerClass parent; /* Google devs feel free to insert new IServiceManager methods in * the middle (probably just because it looks nice) which shifts * the transaction codes and makes this headache for us. */ guint32 check_service_transaction; guint32 add_service_transaction; guint32 list_services_transaction; guint32 register_for_notifications_transaction; guint32 unregister_for_notifications_transaction; GBinderLocalRequest* (*list_services_req) (GBinderClient* client, gint32 index); GBinderLocalRequest* (*add_service_req) (GBinderClient* client, const char* name, GBinderLocalObject* obj); } GBinderServiceManagerAidlClass; #define GBINDER_TYPE_SERVICEMANAGER_AIDL \ gbinder_servicemanager_aidl_get_type() #define GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) \ G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \ GBinderServiceManagerAidlClass) #define GBINDER_TYPE_SERVICEMANAGER_AIDL2 \ gbinder_servicemanager_aidl2_get_type() #define GBINDER_TYPE_SERVICEMANAGER_AIDL3 \ gbinder_servicemanager_aidl3_get_type() /* AIDL4 is missing for historical reasons */ #define GBINDER_TYPE_SERVICEMANAGER_AIDL5 \ gbinder_servicemanager_aidl5_get_type() #define DUMP_FLAG_PRIORITY_DEFAULT (0x08) #define DUMP_FLAG_PRIORITY_ALL (0x0f) #endif /* GBINDER_SERVICEMANAGER_AIDL_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl2.c000066400000000000000000000066301526127616700224200ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_aidl.h" #include #include /* Variant of AIDL servicemanager appeared in Android 9 (API level 28) */ typedef GBinderServiceManagerAidl GBinderServiceManagerAidl2; typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl2Class; G_DEFINE_TYPE(GBinderServiceManagerAidl2, gbinder_servicemanager_aidl2, GBINDER_TYPE_SERVICEMANAGER_AIDL) static GBinderLocalRequest* gbinder_servicemanager_aidl2_list_services_req( GBinderClient* client, gint32 index) { GBinderLocalRequest* req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, index); gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL); return req; } static GBinderLocalRequest* gbinder_servicemanager_aidl2_add_service_req( GBinderClient* client, const char* name, GBinderLocalObject* obj) { GBinderLocalRequest* req = gbinder_client_new_request(client); gbinder_local_request_append_string16(req, name); gbinder_local_request_append_local_object(req, obj); gbinder_local_request_append_int32(req, 0); gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_DEFAULT); return req; } static void gbinder_servicemanager_aidl2_init( GBinderServiceManagerAidl* self) { } static void gbinder_servicemanager_aidl2_class_init( GBinderServiceManagerAidl2Class* klass) { GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass); gbinder_servicemanager_class_common_init(manager); klass->list_services_req = gbinder_servicemanager_aidl2_list_services_req; klass->add_service_req = gbinder_servicemanager_aidl2_add_service_req; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl3.c000066400000000000000000000121441526127616700224160ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2022 Slava Monich * Copyright (C) 2021 Gary Wang * Copyright (C) 2021 Madhushan Nishantha * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_aidl.h" #include "gbinder_client_p.h" #include #include /* Variant of AIDL servicemanager appeared in Android 11 (API level 30) */ typedef GBinderServiceManagerAidl GBinderServiceManagerAidl3; typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl3Class; G_DEFINE_TYPE(GBinderServiceManagerAidl3, gbinder_servicemanager_aidl3, GBINDER_TYPE_SERVICEMANAGER_AIDL2) #define GET_THIS_CLASS(obj) GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) static GBinderRemoteObject* gbinder_servicemanager_aidl3_get_service( GBinderServiceManager* manager, const char* name, int* status, const GBinderIpcSyncApi* api) { GBinderClient* client = manager->client; GBinderLocalRequest* req = gbinder_client_new_request(client); GBinderRemoteObject* obj; GBinderRemoteReply* reply; GBinderReader reader; GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(manager); gbinder_local_request_append_string16(req, name); reply = gbinder_client_transact_sync_reply2(client, klass->check_service_transaction, req, status, api); gbinder_remote_reply_init_reader(reply, &reader); gbinder_reader_read_int32(&reader, NULL /* status? */); obj = gbinder_reader_read_object(&reader); gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); return obj; } static char** gbinder_servicemanager_aidl3_list( GBinderServiceManager* manager, const GBinderIpcSyncApi* api) { GPtrArray* list = g_ptr_array_new(); GBinderClient* client = manager->client; GBinderRemoteReply* reply; GBinderLocalRequest* req = gbinder_client_new_request(client); GBinderServiceManagerAidlClass* klass = GET_THIS_CLASS(manager); /* * Starting from Android 11, no `index` field is required but * only with `dump priority` field to request to list services. * As a result, a vector of strings which stands for service * list is given in the binder response. */ gbinder_local_request_append_int32(req, DUMP_FLAG_PRIORITY_ALL); reply = gbinder_client_transact_sync_reply2(client, klass->list_services_transaction, req, NULL, api); if (reply) { GBinderReader reader; gint32 count; gbinder_remote_reply_init_reader(reply, &reader); gbinder_reader_read_int32(&reader, NULL /* status */); if (gbinder_reader_read_int32(&reader, &count)) { int i; /* Iterate each service name */ for (i = 0; i < count; i++) { g_ptr_array_add(list, gbinder_reader_read_string16(&reader)); } } gbinder_remote_reply_unref(reply); } gbinder_local_request_unref(req); g_ptr_array_add(list, NULL); return (char**)g_ptr_array_free(list, FALSE); } static void gbinder_servicemanager_aidl3_init( GBinderServiceManagerAidl3* self) { } static void gbinder_servicemanager_aidl3_class_init( GBinderServiceManagerAidl3Class* klass) { GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass); gbinder_servicemanager_class_common_init(manager); manager->list = gbinder_servicemanager_aidl3_list; manager->get_service = gbinder_servicemanager_aidl3_get_service; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl5.c000066400000000000000000000133341526127616700224220ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2022 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_aidl.h" #include "gbinder_client_p.h" #include #include #include /* Variant of AIDL servicemanager appeared in Android 15 (API level 35) */ typedef GBinderServiceManagerAidl GBinderServiceManagerAidl5; typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl5Class; G_DEFINE_TYPE(GBinderServiceManagerAidl5, gbinder_servicemanager_aidl5, /* AIDL4 is missing for historical reasons */ GBINDER_TYPE_SERVICEMANAGER_AIDL3) enum gbinder_servicemanager_aidl5_calls { AIDL5_GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, AIDL5_GET_SERVICE2_TRANSACTION, AIDL5_CHECK_SERVICE_TRANSACTION, AIDL5_ADD_SERVICE_TRANSACTION, AIDL5_LIST_SERVICES_TRANSACTION, AIDL5_REGISTER_FOR_NOTIFICATIONS_TRANSACTION, AIDL5_UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION }; /* * Note that this weird get_service implementation is inherited by aidl6 * implementation and it's fine - who knows if Google guys at some point * decide to replace IBinder with Service again. Even if they never do, * this implementation is optimized for IBinder which is the current * Android 16 behavior, at least at the time of this writing. */ static GBinderRemoteObject* gbinder_servicemanager_aidl5_get_service( GBinderServiceManager* manager, const char* name, int* status, const GBinderIpcSyncApi* api) { GBinderClient* client = manager->client; GBinderLocalRequest* req = gbinder_client_new_request(client); GBinderRemoteObject* obj = NULL; GBinderRemoteReply* reply; GBinderReader reader; GBinderServiceManagerAidlClass* klass = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(manager); gbinder_local_request_append_string16(req, name); reply = gbinder_client_transact_sync_reply2(client, klass->check_service_transaction, req, status, api); gbinder_remote_reply_init_reader(reply, &reader); gbinder_reader_read_int32(&reader, NULL /* status? */); /* * Could be either of these: * * @nullable IBinder checkService(@utf8InCpp String name); * Service checkService(@utf8InCpp String name); * * The return value has mutated during Android 15 lifetime. */ if (!gbinder_reader_read_nullable_object(&reader, &obj)) { gint32 tag; /* * If it's not IBinder then it must be a Service: * * union Service { * ServiceWithMetadata serviceWithMetadata; // tag = 0 * @nullable IBinder accessor; * } * * parcelable ServiceWithMetadata { * @nullable IBinder service; * boolean isLazyService; * } */ if (gbinder_reader_read_int32(&reader, NULL /* what is it? */) && gbinder_reader_read_int32(&reader, &tag) && tag == 0) { GBinderReader parcel; if (gbinder_reader_start_parcelable(&reader, &parcel, NULL)) { obj = gbinder_reader_read_object(&parcel); gbinder_reader_finish_parcelable(&parcel); } } } gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); return obj; } static void gbinder_servicemanager_aidl5_init( GBinderServiceManagerAidl5* self) { } static void gbinder_servicemanager_aidl5_class_init( GBinderServiceManagerAidl5Class* klass) { GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass); gbinder_servicemanager_class_common_init(manager); manager->get_service = gbinder_servicemanager_aidl5_get_service; klass->check_service_transaction = AIDL5_CHECK_SERVICE_TRANSACTION; klass->add_service_transaction = AIDL5_ADD_SERVICE_TRANSACTION; klass->list_services_transaction = AIDL5_LIST_SERVICES_TRANSACTION; klass->register_for_notifications_transaction = AIDL5_REGISTER_FOR_NOTIFICATIONS_TRANSACTION; klass->unregister_for_notifications_transaction = AIDL5_UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_aidl6.c000066400000000000000000000063411526127616700224230ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2022 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * 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_servicemanager_aidl.h" /* Variant of AIDL servicemanager appeared in Android 16 (API level 36) */ typedef GBinderServiceManagerAidl GBinderServiceManagerAidl6; typedef GBinderServiceManagerAidlClass GBinderServiceManagerAidl6Class; G_DEFINE_TYPE(GBinderServiceManagerAidl6, gbinder_servicemanager_aidl6, GBINDER_TYPE_SERVICEMANAGER_AIDL5) enum gbinder_servicemanager_aidl6_calls { AIDL6_GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, AIDL6_GET_SERVICE2_TRANSACTION, AIDL6_CHECK_SERVICE_TRANSACTION, AIDL6_CHECK_SERVICE2_TRANSACTION, AIDL6_ADD_SERVICE_TRANSACTION, AIDL6_LIST_SERVICES_TRANSACTION, AIDL6_REGISTER_FOR_NOTIFICATIONS_TRANSACTION, AIDL6_UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION }; static void gbinder_servicemanager_aidl6_init( GBinderServiceManagerAidl6* self) { } static void gbinder_servicemanager_aidl6_class_init( GBinderServiceManagerAidl6Class* klass) { GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass); gbinder_servicemanager_class_common_init(manager); klass->check_service_transaction = AIDL6_CHECK_SERVICE_TRANSACTION; klass->add_service_transaction = AIDL6_ADD_SERVICE_TRANSACTION; klass->list_services_transaction = AIDL6_LIST_SERVICES_TRANSACTION; klass->register_for_notifications_transaction = AIDL6_REGISTER_FOR_NOTIFICATIONS_TRANSACTION; klass->unregister_for_notifications_transaction = AIDL6_UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_hidl.c000066400000000000000000000356141526127616700223510ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * 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_servicemanager_p.h" #include "gbinder_client_p.h" #include "gbinder_log.h" #include #include #include #include #include #include typedef struct gbinder_servicemanager_hidl_watch { char* name; GBinderLocalObject* callback; GBinderClient* client; gulong tx_id; } GBinderServiceManagerHidlWatch; typedef struct gbinder_servicemanager_hidl_watch_call { GBinderLocalObject* callback; GBinderServiceManagerHidlWatch* watch; } GBinderServiceManagerHidlWatchCall; typedef GBinderServiceManagerClass GBinderServiceManagerHidlClass; typedef struct gbinder_servicemanager_hidl { GBinderServiceManager manager; GHashTable* watch_table; } GBinderServiceManagerHidl; G_DEFINE_TYPE(GBinderServiceManagerHidl, gbinder_servicemanager_hidl, GBINDER_TYPE_SERVICEMANAGER) #define PARENT_CLASS gbinder_servicemanager_hidl_parent_class #define GBINDER_TYPE_SERVICEMANAGER_HIDL \ gbinder_servicemanager_hidl_get_type() #define GBINDER_SERVICEMANAGER_HIDL(obj) \ G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER_HIDL, \ GBinderServiceManagerHidl) enum gbinder_servicemanager_hidl_calls { GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, ADD_TRANSACTION, GET_TRANSPORT_TRANSACTION, LIST_TRANSACTION, LIST_BY_INTERFACE_TRANSACTION, REGISTER_FOR_NOTIFICATIONS_TRANSACTION, DEBUG_DUMP_TRANSACTION, REGISTER_PASSTHROUGH_CLIENT_TRANSACTION }; enum gbinder_servicemanager_hidl_notifications { ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION }; #define SERVICEMANAGER_HIDL_IFACE "android.hidl.manager@1.0::IServiceManager" #define SERVICEMANAGER_HIDL_NOTIFICATION_IFACE \ "android.hidl.manager@1.0::IServiceNotification" static void gbinder_servicemanager_hidl_handle_registration( GBinderServiceManagerHidl* self, GBinderReader* reader) { char* fqname = gbinder_reader_read_hidl_string(reader); char* name = gbinder_reader_read_hidl_string(reader); gboolean preexisting; /* (string fqName, string name, bool preexisting) */ if (fqname && name && gbinder_reader_read_bool(reader, &preexisting) && gbinder_reader_at_end(reader)) { char* full_name = g_strconcat(fqname, "/", name, NULL); GDEBUG("%s %s", full_name, preexisting ? "true" : "false"); gbinder_servicemanager_service_registered(&self->manager, full_name); g_free(full_name); } else { GWARN("Failed to parse IServiceNotification::onRegistration payload"); } g_free(fqname); g_free(name); } static GBinderLocalReply* gbinder_servicemanager_hidl_notification( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(user_data); const char* iface = gbinder_remote_request_interface(req); if (!g_strcmp0(iface, SERVICEMANAGER_HIDL_NOTIFICATION_IFACE)) { GBinderReader reader; gbinder_remote_request_init_reader(req, &reader); switch (code) { case ON_REGISTRATION_TRANSACTION: GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u onRegistration", code); gbinder_servicemanager_hidl_handle_registration(self, &reader); *status = GBINDER_STATUS_OK; break; default: GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u", code); *status = GBINDER_STATUS_FAILED; break; } } else { GDEBUG("%s %u", iface, code); *status = GBINDER_STATUS_FAILED; } return NULL; } static char** gbinder_servicemanager_hidl_list( GBinderServiceManager* self, const GBinderIpcSyncApi* api) { GBinderLocalRequest* req = gbinder_client_new_request(self->client); GBinderRemoteReply* reply = gbinder_client_transact_sync_reply2 (self->client, LIST_TRANSACTION, req, NULL, api); gbinder_local_request_unref(req); if (reply) { GBinderReader reader; char** result = NULL; int status = -1; gbinder_remote_reply_init_reader(reply, &reader); /* Read status */ GVERIFY(gbinder_reader_read_int32(&reader, &status)); GASSERT(status == GBINDER_STATUS_OK); /* Followed by hidl_vec */ result = gbinder_reader_read_hidl_string_vec(&reader); gbinder_remote_reply_unref(reply); return result; } return NULL; } static GBinderRemoteObject* gbinder_servicemanager_hidl_get_service( GBinderServiceManager* self, const char* fqinstance, int* status, const GBinderIpcSyncApi* api) { /* e.g. "android.hardware.radio@1.1::IRadio/slot1" */ const char* sep = strchr(fqinstance, '/'); GBinderRemoteObject* obj = NULL; if (sep) { GBinderRemoteReply* reply; GBinderLocalRequest* req = gbinder_client_new_request(self->client); char* fqname = g_strndup(fqinstance, sep - fqinstance); const char* name = sep + 1; gbinder_local_request_append_hidl_string(req, fqname); gbinder_local_request_append_hidl_string(req, name); reply = gbinder_client_transact_sync_reply2(self->client, GET_TRANSACTION, req, status, api); if (reply) { GBinderReader reader; int status = -1; gbinder_remote_reply_init_reader(reply, &reader); /* Read status */ GVERIFY(gbinder_reader_read_int32(&reader, &status)); GASSERT(status == GBINDER_STATUS_OK); /* Read the object */ obj = gbinder_reader_read_object(&reader); gbinder_remote_reply_unref(reply); } gbinder_local_request_unref(req); g_free(fqname); } else { GERR("Invalid instance \"%s\"", fqinstance); if (status) *status = (-EINVAL); } return obj; } static int gbinder_servicemanager_hidl_add_service( GBinderServiceManager* self, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api) { int status; GBinderRemoteReply* reply; GBinderLocalRequest* req = gbinder_client_new_request(self->client); /* add(string name, interface service) generates (bool success); */ gbinder_local_request_append_hidl_string(req, name); gbinder_local_request_append_local_object(req, obj); reply = gbinder_client_transact_sync_reply2(self->client, ADD_TRANSACTION, req, &status, api); gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); return status; } static void gbinder_servicemanager_hidl_watch_free( gpointer data) { GBinderServiceManagerHidlWatch* watch = data; gbinder_local_object_drop(watch->callback); gbinder_client_cancel(watch->client, watch->tx_id); gbinder_client_unref(watch->client); g_free(watch->name); g_free(watch); } static void gbinder_servicemanager_hidl_watch_call_reply( GBinderClient* client, GBinderRemoteReply* reply, int tx_status, void* user_data) { GBinderServiceManagerHidlWatchCall* call = user_data; GBinderServiceManagerHidlWatch* watch = call->watch; /* * We can only get here if the call wasn't cancelled by * gbinder_servicemanager_hidl_watch_free() meaning that * we can safely access GBinderServiceManagerHidlWatch. */ watch->tx_id = 0; if (tx_status == GBINDER_STATUS_OK) { GBinderReader reader; gint32 status; gboolean success; gbinder_remote_reply_init_reader(reply, &reader); if (gbinder_reader_read_int32(&reader, &status) && gbinder_reader_read_bool(&reader, &success)) { if (status == GBINDER_STATUS_OK && success) { /* Successfully registered */ return; } else { GWARN("registerForNotifications(%s) failed", watch->name); } } else { GWARN("Unexpected registerForNotifications(%s) reply", watch->name); } } else { GWARN("registerForNotifications(%s) tx failed", watch->name); } /* There's no need to keep this object around if its registration failed */ gbinder_local_object_drop(watch->callback); watch->callback = NULL; } static void gbinder_servicemanager_hidl_watch_call_destroy( void* user_data) { GBinderServiceManagerHidlWatchCall* call = user_data; /* call->watch may be destroyed by now, don't touch it */ gbinder_local_object_unref(call->callback); g_free(call); } static GBINDER_SERVICEMANAGER_NAME_CHECK gbinder_servicemanager_hidl_check_name( GBinderServiceManager* self, const char* name) { if (name) { const gsize len = strlen(name); static const char allowed_chars[] = "./0123456789:@" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; if (len && strspn(name, allowed_chars) == len) { return strchr(name, '/') ? GBINDER_SERVICEMANAGER_NAME_NORMALIZE : GBINDER_SERVICEMANAGER_NAME_OK; } } return GBINDER_SERVICEMANAGER_NAME_INVALID; } static char* gbinder_servicemanager_hidl_normalize_name( GBinderServiceManager* self, const char* name) { /* Slash must be there, see gbinder_servicemanager_hidl_check_name() */ return g_strndup(name, strchr(name, '/') - name); } static gboolean gbinder_servicemanager_hidl_watch( GBinderServiceManager* manager, const char* name) { GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(manager); GBinderLocalRequest* req = gbinder_client_new_request(manager->client); GBinderServiceManagerHidlWatch* watch = g_new0(GBinderServiceManagerHidlWatch, 1); GBinderServiceManagerHidlWatchCall* call = g_new0(GBinderServiceManagerHidlWatchCall, 1); watch->name = g_strdup(name); watch->client = gbinder_client_ref(manager->client); watch->callback = gbinder_servicemanager_new_local_object(manager, SERVICEMANAGER_HIDL_NOTIFICATION_IFACE, gbinder_servicemanager_hidl_notification, self); g_hash_table_replace(self->watch_table, watch->name, watch); /* registerForNotifications(string fqName, string name, * IServiceNotification callback) generates (bool success); */ gbinder_local_request_append_hidl_string(req, name); gbinder_local_request_append_hidl_string(req, ""); gbinder_local_request_append_local_object(req, watch->callback); /* * GBinderServiceManagerHidlWatchCall keeps the additional reference * to the callback object for the duration of the transaction. Since * the transaction is asynchronous, the object may be unrefed before * the transaction actually gets submitted, and we don't want to pass * an invalid object pointer to the kernel. This extra ref makes sure * that the pointer remains valid. */ call->watch = watch; call->callback = gbinder_local_object_ref(watch->callback); watch->tx_id = gbinder_client_transact(watch->client, REGISTER_FOR_NOTIFICATIONS_TRANSACTION, 0, req, gbinder_servicemanager_hidl_watch_call_reply, gbinder_servicemanager_hidl_watch_call_destroy, call); gbinder_local_request_unref(req); if (watch->tx_id) { /* * At this point, we don't really know whether the registration * will actually succeed. Since in the worst case we just won't * get any notifications, I don't see a good enough reason to * complicate the internal interface to handle such an unlikely * and non-critical issue. */ return TRUE; } else { /* unwatch() won't be called if we return FALSE */ g_hash_table_remove(self->watch_table, watch->name); return FALSE; } } static void gbinder_servicemanager_hidl_unwatch( GBinderServiceManager* manager, const char* name) { g_hash_table_remove(GBINDER_SERVICEMANAGER_HIDL(manager)-> watch_table, name); } static void gbinder_servicemanager_hidl_init( GBinderServiceManagerHidl* self) { self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, gbinder_servicemanager_hidl_watch_free); } static void gbinder_servicemanager_hidl_finalize( GObject* object) { GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(object); g_hash_table_destroy(self->watch_table); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_servicemanager_hidl_class_init( GBinderServiceManagerHidlClass* klass) { gbinder_servicemanager_class_common_init(klass); klass->iface = SERVICEMANAGER_HIDL_IFACE; klass->default_device = GBINDER_DEFAULT_HWBINDER; klass->list = gbinder_servicemanager_hidl_list; klass->get_service = gbinder_servicemanager_hidl_get_service; klass->add_service = gbinder_servicemanager_hidl_add_service; klass->check_name = gbinder_servicemanager_hidl_check_name; klass->normalize_name = gbinder_servicemanager_hidl_normalize_name; klass->watch = gbinder_servicemanager_hidl_watch; klass->unwatch = gbinder_servicemanager_hidl_unwatch; G_OBJECT_CLASS(klass)->finalize = gbinder_servicemanager_hidl_finalize; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicemanager_p.h000066400000000000000000000112071526127616700216650ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SERVICEMANAGER_PRIVATE_H #define GBINDER_SERVICEMANAGER_PRIVATE_H #include "gbinder_types_p.h" #include #include typedef struct gbinder_servicemanager_priv GBinderServiceManagerPriv; struct gbinder_servicemanager { GObject parent; GBinderServiceManagerPriv* priv; const char* dev; GBinderClient* client; }; typedef enum gbinder_servicemanager_name_check { GBINDER_SERVICEMANAGER_NAME_OK, GBINDER_SERVICEMANAGER_NAME_NORMALIZE, GBINDER_SERVICEMANAGER_NAME_INVALID, } GBINDER_SERVICEMANAGER_NAME_CHECK; typedef struct gbinder_servicemanager_class { GObjectClass parent; GMutex mutex; GHashTable* table; const char* iface; const char* default_device; /* Methods (synchronous, may be invoked on a worker thread) */ char** (*list)(GBinderServiceManager* self, const GBinderIpcSyncApi* api); GBinderRemoteObject* (*get_service)(GBinderServiceManager* self, const char* name, int* status, const GBinderIpcSyncApi* api); int (*add_service)(GBinderServiceManager* self, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api); /* Checking/normalizing watch names */ GBINDER_SERVICEMANAGER_NAME_CHECK (*check_name) (GBinderServiceManager* self, const char* name); char* (*normalize_name)(GBinderServiceManager* self, const char* name); /* If watch() returns FALSE, unwatch() is not called */ gboolean (*watch)(GBinderServiceManager* self, const char* name); void (*unwatch)(GBinderServiceManager* self, const char* name); } GBinderServiceManagerClass; GType gbinder_servicemanager_get_type(void) GBINDER_INTERNAL; #define GBINDER_TYPE_SERVICEMANAGER (gbinder_servicemanager_get_type()) #define GBINDER_SERVICEMANAGER_CLASS(klass) \ G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER, \ GBinderServiceManagerClass) #define gbinder_servicemanager_ipc(sm) gbinder_client_ipc(sm->client) GBinderServiceManager* gbinder_servicemanager_new_with_type( GType type, const char* dev, const char* rpc_protocol) GBINDER_INTERNAL; void gbinder_servicemanager_service_registered( GBinderServiceManager* self, const char* name) GBINDER_INTERNAL; void gbinder_servicemanager_class_common_init( GBinderServiceManagerClass* klass) GBINDER_INTERNAL; /* Declared for unit tests */ void gbinder_servicemanager_exit( void) GBINDER_INTERNAL GBINDER_DESTRUCTOR; /* Derived types */ GType gbinder_servicemanager_aidl_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl2_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl3_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl5_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_aidl6_get_type(void) GBINDER_INTERNAL; GType gbinder_servicemanager_hidl_get_type(void) GBINDER_INTERNAL; #endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicename.c000066400000000000000000000145151526127616700206540ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Jolla Ltd. * Copyright (C) 2019-2021 Slava Monich * * 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_types_p.h" #include "gbinder_eventloop_p.h" #include "gbinder_servicename.h" #include "gbinder_servicemanager.h" #include "gbinder_local_object.h" #include "gbinder_log.h" #include /* Since 1.0.26 */ #define GBINDER_SERVICENAME_RETRY_INTERVAL_MS (500) typedef struct gbinder_servicename_priv { GBinderServiceName pub; gint refcount; char* name; GBinderLocalObject* object; GBinderServiceManager* sm; GBinderEventLoopTimeout* retry_timer; gulong presence_id; gulong add_call_id; } GBinderServiceNamePriv; static void gbinder_servicename_add_service( GBinderServiceNamePriv* priv); GBINDER_INLINE_FUNC GBinderServiceNamePriv* gbinder_servicename_cast(GBinderServiceName* pub) { return G_CAST(pub, GBinderServiceNamePriv, pub); } /*==========================================================================* * Implementation *==========================================================================*/ static gboolean gbinder_servicename_add_service_retry( gpointer user_data) { GBinderServiceNamePriv* priv = user_data; priv->retry_timer = NULL; gbinder_servicename_add_service(priv); return G_SOURCE_REMOVE; } static void gbinder_servicename_add_service_done( GBinderServiceManager* sm, int status, void* user_data) { GBinderServiceNamePriv* priv = user_data; GASSERT(priv->add_call_id); priv->add_call_id = 0; if (status) { GWARN("Error %d adding name \"%s\"", status, priv->name); gbinder_timeout_remove(priv->retry_timer); priv->retry_timer = gbinder_timeout_add(GBINDER_SERVICENAME_RETRY_INTERVAL_MS, gbinder_servicename_add_service_retry, priv); } else { GDEBUG("Service \"%s\" has been registered", priv->name); } } static void gbinder_servicename_add_service( GBinderServiceNamePriv* priv) { GDEBUG("Adding service \"%s\"", priv->name); gbinder_servicemanager_cancel(priv->sm, priv->add_call_id); priv->add_call_id = gbinder_servicemanager_add_service(priv->sm, priv->name, priv->object, gbinder_servicename_add_service_done, priv); } static void gbinder_servicename_presence_handler( GBinderServiceManager* sm, void* user_data) { GBinderServiceNamePriv* priv = user_data; if (gbinder_servicemanager_is_present(sm)) { gbinder_servicename_add_service(priv); } else { if (priv->add_call_id) { gbinder_servicemanager_cancel(priv->sm, priv->add_call_id); priv->add_call_id = 0; } if (priv->retry_timer) { gbinder_timeout_remove(priv->retry_timer); priv->retry_timer = NULL; } } } /*==========================================================================* * Interface *==========================================================================*/ GBinderServiceName* gbinder_servicename_new( GBinderServiceManager* sm, GBinderLocalObject* object, const char* name) { if (G_LIKELY(sm) && G_LIKELY(object) && G_LIKELY(name)) { GBinderServiceNamePriv* priv = g_slice_new0(GBinderServiceNamePriv); GBinderServiceName* self = &priv->pub; g_atomic_int_set(&priv->refcount, 1); priv->object = gbinder_local_object_ref(object); priv->sm = gbinder_servicemanager_ref(sm); self->name = priv->name = g_strdup(name); priv->presence_id = gbinder_servicemanager_add_presence_handler(sm, gbinder_servicename_presence_handler, priv); if (gbinder_servicemanager_is_present(sm)) { gbinder_servicename_add_service(priv); } return self; } else { return NULL; } } GBinderServiceName* gbinder_servicename_ref( GBinderServiceName* self) { if (G_LIKELY(self)) { GBinderServiceNamePriv* priv = gbinder_servicename_cast(self); GASSERT(priv->refcount > 0); g_atomic_int_inc(&priv->refcount); } return self; } void gbinder_servicename_unref( GBinderServiceName* self) { if (G_LIKELY(self)) { GBinderServiceNamePriv* priv = gbinder_servicename_cast(self); GASSERT(priv->refcount > 0); if (g_atomic_int_dec_and_test(&priv->refcount)) { gbinder_servicemanager_cancel(priv->sm, priv->add_call_id); gbinder_servicemanager_remove_handler(priv->sm, priv->presence_id); gbinder_servicemanager_unref(priv->sm); gbinder_local_object_unref(priv->object); gbinder_timeout_remove(priv->retry_timer); g_free(priv->name); gutil_slice_free(priv); } } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicepoll.c000066400000000000000000000172101526127616700206750ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "gbinder_servicepoll.h" #include "gbinder_servicemanager.h" #include "gbinder_eventloop_p.h" #include #include /* This is configurable mostly so that unit testing doesn't take too long */ guint gbinder_servicepoll_interval_ms = 2000; typedef GObjectClass GBinderServicePollClass; struct gbinder_servicepoll { GObject object; GBinderServiceManager* manager; char** list; gulong list_id; GBinderEventLoopTimeout* timer; }; #define PARENT_CLASS gbinder_servicepoll_parent_class #define THIS_TYPE gbinder_servicepoll_get_type() #define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, GBinderServicePoll) GType THIS_TYPE GBINDER_INTERNAL; G_DEFINE_TYPE(GBinderServicePoll, gbinder_servicepoll, G_TYPE_OBJECT) enum gbinder_servicepoll_signal { SIGNAL_NAME_ADDED, SIGNAL_COUNT }; static const char SIGNAL_NAME_ADDED_NAME[] = "servicepoll-name-added"; static guint gbinder_servicepoll_signals[SIGNAL_COUNT] = { 0 }; /*==========================================================================* * Implementation *==========================================================================*/ /* GBinderServiceManagerListFunc callback returns TRUE to keep the services * list, otherwise the caller will deallocate it. */ gboolean gbinder_servicepoll_list( GBinderServiceManager* sm, char** services, void* user_data) { GBinderServicePoll* self = THIS(user_data); gbinder_servicepoll_ref(self); self->list_id = 0; if (services) { const GStrV* ptr_new; ptr_new = services = gutil_strv_sort(services, TRUE); if (self->list) { const GStrV* ptr_old = self->list; while (*ptr_new && *ptr_old) { const int i = gutil_strv_find(ptr_old, *ptr_new); if (i < 0) { /* New name */ g_signal_emit(self, gbinder_servicepoll_signals [SIGNAL_NAME_ADDED], 0, *ptr_new); } else { int k; /* If some names have disappeared, then i may be > 0 */ for (k = 0; k < i; k ++) ptr_old++; ptr_old++; } ptr_new++; } } while (*ptr_new) { g_signal_emit(self, gbinder_servicepoll_signals [SIGNAL_NAME_ADDED], 0, *ptr_new); ptr_new++; } } g_strfreev(self->list); self->list = services; gbinder_servicepoll_unref(self); return TRUE; } static gboolean gbinder_servicepoll_timer( gpointer user_data) { GBinderServicePoll* self = THIS(user_data); if (!self->list_id) { self->list_id = gbinder_servicemanager_list(self->manager, gbinder_servicepoll_list, self); } return G_SOURCE_CONTINUE; } static GBinderServicePoll* gbinder_servicepoll_create( GBinderServiceManager* manager) { GBinderServicePoll* self = g_object_new(THIS_TYPE, NULL); self->manager = gbinder_servicemanager_ref(manager); self->list_id = gbinder_servicemanager_list(manager, gbinder_servicepoll_list, self); return self; } /*==========================================================================* * API *==========================================================================*/ GBinderServicePoll* gbinder_servicepoll_new( GBinderServiceManager* manager, GBinderServicePoll** weakptr) { if (weakptr) { if (*weakptr) { gbinder_servicepoll_ref(*weakptr); } else { *weakptr = gbinder_servicepoll_create(manager); g_object_add_weak_pointer(G_OBJECT(*weakptr), (gpointer*)weakptr); } return *weakptr; } else { return gbinder_servicepoll_create(manager); } } GBinderServicePoll* gbinder_servicepoll_ref( GBinderServicePoll* self) { if (G_LIKELY(self)) { g_object_ref(THIS(self)); return self; } else { return NULL; } } void gbinder_servicepoll_unref( GBinderServicePoll* self) { if (G_LIKELY(self)) { g_object_unref(THIS(self)); } } GBinderServiceManager* gbinder_servicepoll_manager( GBinderServicePoll* self) { return G_LIKELY(self) ? self->manager : NULL; } gboolean gbinder_servicepoll_is_known_name( GBinderServicePoll* self, const char* name) { return G_LIKELY(self) && gutil_strv_contains(self->list, name); } gulong gbinder_servicepoll_add_handler( GBinderServicePoll* self, GBinderServicePollFunc fn, void* user_data) { return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self, SIGNAL_NAME_ADDED_NAME, G_CALLBACK(fn), user_data) : 0; } void gbinder_servicepoll_remove_handler( GBinderServicePoll* self, gulong id) { if (G_LIKELY(self) && G_LIKELY(id)) { g_signal_handler_disconnect(self, id); } } /*==========================================================================* * Internals *==========================================================================*/ static void gbinder_servicepoll_init( GBinderServicePoll* self) { self->timer = gbinder_timeout_add(gbinder_servicepoll_interval_ms, gbinder_servicepoll_timer, self); } static void gbinder_servicepoll_finalize( GObject* object) { GBinderServicePoll* self = THIS(object); gbinder_timeout_remove(self->timer); gbinder_servicemanager_cancel(self->manager, self->list_id); gbinder_servicemanager_unref(self->manager); g_strfreev(self->list); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void gbinder_servicepoll_class_init( GBinderServicePollClass* klass) { G_OBJECT_CLASS(klass)->finalize = gbinder_servicepoll_finalize; gbinder_servicepoll_signals[SIGNAL_NAME_ADDED] = g_signal_new(SIGNAL_NAME_ADDED_NAME, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_servicepoll.h000066400000000000000000000055501526127616700207060ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SERVICEPOLL_H #define GBINDER_SERVICEPOLL_H #include "gbinder_types_p.h" extern guint gbinder_servicepoll_interval_ms GBINDER_INTERNAL; typedef void (*GBinderServicePollFunc)( GBinderServicePoll* poll, const char* name_added, void* user_data); GBinderServicePoll* gbinder_servicepoll_new( GBinderServiceManager* manager, GBinderServicePoll** weakptr) GBINDER_INTERNAL; GBinderServicePoll* gbinder_servicepoll_ref( GBinderServicePoll* poll) GBINDER_INTERNAL; void gbinder_servicepoll_unref( GBinderServicePoll* poll) GBINDER_INTERNAL; GBinderServiceManager* gbinder_servicepoll_manager( GBinderServicePoll* poll) GBINDER_INTERNAL; gboolean gbinder_servicepoll_is_known_name( GBinderServicePoll* poll, const char* name) GBINDER_INTERNAL; gulong gbinder_servicepoll_add_handler( GBinderServicePoll* poll, GBinderServicePollFunc func, void* user_data) GBINDER_INTERNAL; void gbinder_servicepoll_remove_handler( GBinderServicePoll* poll, gulong id) GBINDER_INTERNAL; #endif /* GBINDER_SERVICEPOLL_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_system.c000066400000000000000000000047451526127616700177030ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * 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_system.h" #include #include #include #include #include int gbinder_system_open( const char* path, int flags) { return open(path, flags); } int gbinder_system_close( int fd) { return close(fd); } int gbinder_system_ioctl( int fd, int request, void* data) { int ret; while ((ret = ioctl(fd, request, data)) < 0 && errno == EINTR); return ret >= 0 ? 0 : -errno; } void* gbinder_system_mmap( size_t length, int prot, int flags, int fd) { return mmap(NULL, length, prot, flags, fd, 0); } int gbinder_system_munmap( void* addr, size_t length) { return munmap(addr, length); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_system.h000066400000000000000000000044561526127616700177070ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_SYSTEM_H #define GBINDER_SYSTEM_H #include "gbinder_types_p.h" int gbinder_system_open( const char* path, int flags) GBINDER_INTERNAL; int gbinder_system_close( int fd) GBINDER_INTERNAL; int gbinder_system_ioctl( int fd, int request, void* data) GBINDER_INTERNAL; void* gbinder_system_mmap( size_t length, int prot, int flags, int fd) GBINDER_INTERNAL; int gbinder_system_munmap( void* addr, size_t length) GBINDER_INTERNAL; #endif /* GBINDER_SYSTEM_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_types_p.h000066400000000000000000000103521526127616700200360ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_TYPES_PRIVATE_H #define GBINDER_TYPES_PRIVATE_H #include typedef struct gbinder_buffer_contents GBinderBufferContents; typedef struct gbinder_buffer_contents_list GBinderBufferContentsList; typedef struct gbinder_cleanup GBinderCleanup; typedef struct gbinder_driver GBinderDriver; typedef struct gbinder_handler GBinderHandler; typedef struct gbinder_io GBinderIo; typedef struct gbinder_object_converter GBinderObjectConverter; typedef struct gbinder_object_registry GBinderObjectRegistry; typedef struct gbinder_output_data GBinderOutputData; typedef struct gbinder_proxy_object GBinderProxyObject; typedef struct gbinder_rpc_protocol GBinderRpcProtocol; typedef struct gbinder_servicepoll GBinderServicePoll; typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx; typedef struct gbinder_ipc_sync_api GBinderIpcSyncApi; #define GBINDER_INLINE_FUNC static inline #define GBINDER_INTERNAL G_GNUC_INTERNAL #define GBINDER_DESTRUCTOR __attribute__((destructor)) #define GBINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4) #define GBINDER_PING_TRANSACTION GBINDER_TRANSACTION('P','N','G') #define GBINDER_DUMP_TRANSACTION GBINDER_TRANSACTION('D','M','P') #define GBINDER_SHELL_COMMAND_TRANSACTION GBINDER_TRANSACTION('C','M','D') #define GBINDER_INTERFACE_TRANSACTION GBINDER_TRANSACTION('N','T','F') #define GBINDER_SYSPROPS_TRANSACTION GBINDER_TRANSACTION('S','P','R') /* platform/system/tools/hidl/Interface.cpp */ #define HIDL_FOURCC(c2,c3,c4) GBINDER_FOURCC(0x0f,c2,c3,c4) #define HIDL_PING_TRANSACTION HIDL_FOURCC('P','N','G') #define HIDL_DESCRIPTOR_CHAIN_TRANSACTION HIDL_FOURCC('C','H','N') #define HIDL_GET_DESCRIPTOR_TRANSACTION HIDL_FOURCC('D','S','C') #define HIDL_SYSPROPS_CHANGED_TRANSACTION HIDL_FOURCC('S','Y','S') #define HIDL_LINK_TO_DEATH_TRANSACTION HIDL_FOURCC('L','T','D') #define HIDL_UNLINK_TO_DEATH_TRANSACTION HIDL_FOURCC('U','T','D') #define HIDL_SET_HAL_INSTRUMENTATION_TRANSACTION HIDL_FOURCC('I','N','T') #define HIDL_GET_REF_INFO_TRANSACTION HIDL_FOURCC('R','E','F') #define HIDL_DEBUG_TRANSACTION HIDL_FOURCC('D','B','G') #define HIDL_HASH_CHAIN_TRANSACTION HIDL_FOURCC('H','S','H') /* As a special case, ServiceManager's handle is zero */ #define GBINDER_SERVICEMANAGER_HANDLE (0) /* Parcelable flags */ #define kNullParcelableFlag (0) #define kNonNullParcelableFlag (1) #endif /* GBINDER_TYPES_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_writer.c000066400000000000000000001155241526127616700176710ustar00rootroot00000000000000/* * Copyright (C) 2025-2026 Jolla Mobile Ltd * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2024 Jolla Ltd. * * 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_writer_p.h" #include "gbinder_buffer_p.h" #include "gbinder_fmq_p.h" #include "gbinder_local_object.h" #include "gbinder_object_converter.h" #include "gbinder_remote_object.h" #include "gbinder_rpc_protocol.h" #include "gbinder_io.h" #include "gbinder_log.h" #include #include #include #include #include #include #include #include #include typedef struct gbinder_writer_priv { GBinderWriterData* data; guint offset; } GBinderWriterPriv; const GBinderWriterType gbinder_writer_type_byte = { "byte", 1, NULL }; const GBinderWriterType gbinder_writer_type_int32 = { "int32", 4, NULL }; static const GBinderWriterField gbinder_writer_type_hidl_string_f[] = { { "hidl_string.data.str", 0, NULL, gbinder_writer_field_hidl_string_write_buf, NULL }, GBINDER_WRITER_FIELD_END() }; const GBinderWriterType gbinder_writer_type_hidl_string = { "hidl_string", sizeof(GBinderHidlString), gbinder_writer_type_hidl_string_f }; G_STATIC_ASSERT(sizeof(GBinderWriter) >= sizeof(GBinderWriterPriv)); GBINDER_INLINE_FUNC GBinderWriterPriv* gbinder_writer_cast(GBinderWriter* pub) { return (GBinderWriterPriv*)pub; } GBINDER_INLINE_FUNC GBinderWriterData* gbinder_writer_data(GBinderWriter* pub) { return G_LIKELY(pub) ? gbinder_writer_cast(pub)->data : NULL; } static gpointer gbinder_writer_cleanup_add( GBinderWriterData* data, GDestroyNotify destroy, gpointer pointer) { if (!data->cleanup) { data->cleanup = gutil_cleanup_new(); } return gutil_cleanup_add(data->cleanup, destroy, pointer); } static void gbinder_writer_cleanup_add_ptr( GBinderWriterData* data, gpointer pointer) { gbinder_writer_cleanup_add(data, g_free, pointer); } void gbinder_writer_data_set_contents( GBinderWriterData* data, GBinderBuffer* buffer, GBinderObjectConverter* convert) { g_byte_array_set_size(data->bytes, 0); gutil_int_array_set_count(data->offsets, 0); data->buffers_size = 0; gutil_cleanup_clear(data->cleanup); gbinder_writer_data_append_contents(data, buffer, 0, convert); } void gbinder_writer_data_append_contents( GBinderWriterData* data, GBinderBuffer* buffer, gsize off, GBinderObjectConverter* convert) { GBinderBufferContents* contents = gbinder_buffer_contents(buffer); if (contents) { gsize bufsize; GByteArray* dest = data->bytes; const guint8* bufdata = gbinder_buffer_data(buffer, &bufsize); void** objects = gbinder_buffer_objects(buffer); gbinder_writer_cleanup_add(data, (GDestroyNotify) gbinder_buffer_contents_unref, gbinder_buffer_contents_ref(contents)); if (objects && *objects) { const GBinderIo* io = gbinder_buffer_io(buffer); const GBinderRpcProtocol* proto = gbinder_buffer_protocol(buffer); /* GBinderIo must be the same because it's defined by the kernel */ GASSERT(io == data->io); if (!data->offsets) { data->offsets = gutil_int_array_new(); } while (*objects) { const guint8* obj = *objects++; gsize objsize, src_objsize, offset = obj - bufdata; GBinderLocalObject* local; GBinderRemoteObject* remote; void* pointer; guint32 handle; GASSERT(offset >= off && offset < bufsize); if (offset >= bufsize) { GWARN("Invalid object offset %u", (guint)offset); return; } src_objsize = io->object_size(obj, proto); if (src_objsize > bufsize - offset) { GWARN("Invalid object at offset %u", (guint)offset); return; } if (offset > off) { /* Copy serialized data preceeding this object */ g_byte_array_append(dest, bufdata + off, offset - off); off = offset; } /* Offset in the destination buffer */ gutil_int_array_append(data->offsets, dest->len); /* Convert remote object into local if necessary */ if (src_objsize && convert && io->decode_binder_handle(obj, &handle, proto) && (local = gbinder_object_converter_handle_to_local(convert, handle, proto, obj + src_objsize - proto->flat_binder_object_extra))) { const guint pos = dest->len; g_byte_array_set_size(dest, pos + GBINDER_MAX_BINDER_OBJECT_SIZE); objsize = io->encode_local_object(dest->data + pos, local, proto); g_byte_array_set_size(dest, pos + objsize); /* Keep the reference */ gbinder_writer_cleanup_add(data, (GDestroyNotify) gbinder_local_object_unref, local); } else if (src_objsize && convert && (pointer = io->decode_binder_local(obj)) && (remote = gbinder_object_converter_local_to_remote (convert, pointer))) { const guint pos = dest->len; g_byte_array_set_size(dest, pos + GBINDER_MAX_BINDER_OBJECT_SIZE); objsize = io->encode_remote_object(dest->data + pos, remote, proto); g_byte_array_set_size(dest, pos + objsize); gbinder_writer_cleanup_add(data, (GDestroyNotify) gbinder_remote_object_unref, remote); } else { objsize = src_objsize; if (src_objsize) { g_byte_array_append(dest, obj, src_objsize); } } /* Size of each buffer has to be 8-byte aligned */ if (src_objsize) { data->buffers_size += G_ALIGN8(io->object_data_size(obj)); } off += src_objsize; } } if (off < bufsize) { /* Copy remaining data */ g_byte_array_append(dest, bufdata + off, bufsize - off); } } } static void gbinder_writer_data_record_offset( GBinderWriterData* data, guint offset) { if (!data->offsets) { data->offsets = gutil_int_array_new(); } gutil_int_array_append(data->offsets, offset); } void gbinder_writer_init( GBinderWriter* self, GBinderWriterData* data, guint offset) { GBinderWriterPriv* priv = gbinder_writer_cast(self); memset(self, 0, sizeof(*self)); priv->data = data; priv->offset = offset; } const void* gbinder_writer_get_data( GBinderWriter* self, gsize* size) /* Since 1.1.14 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { GByteArray* buf = data->bytes; if (size) { *size = buf->len; } return buf->data; } else { if (size) { *size = 0; } return NULL; } } gsize gbinder_writer_bytes_written( GBinderWriter* self) /* since 1.0.21 */ { GBinderWriterData* data = gbinder_writer_data(self); return G_LIKELY(data) ? data->bytes->len : 0; } static void gbinder_writer_data_append_padded( GByteArray* buf, const void* data, guint size) { /* Primitive values are padded to 4-byte boundary */ const guint padded_size = 4; guint8* ptr; g_byte_array_set_size(buf, buf->len + padded_size); ptr = buf->data + (buf->len - padded_size); memcpy(ptr, data, size); memset(ptr + size, 0, padded_size - size); } void gbinder_writer_append_bool( GBinderWriter* self, gboolean value) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_bool(data, value); } } void gbinder_writer_data_append_bool( GBinderWriterData* data, gboolean value) { const guint8 b = (value != FALSE); gbinder_writer_data_append_padded(data->bytes, &b, sizeof(b)); } void gbinder_writer_append_int8( GBinderWriter* self, guint8 value) /* Since 1.1.15 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_padded(data->bytes, &value, sizeof(value)); } } void gbinder_writer_append_int16( GBinderWriter* self, guint16 value) /* Since 1.1.15 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_padded(data->bytes, &value, sizeof(value)); } } void gbinder_writer_append_int32( GBinderWriter* self, guint32 value) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_int32(data, value); } } void gbinder_writer_data_append_int32( GBinderWriterData* data, guint32 value) { GByteArray* buf = data->bytes; guint32* ptr; g_byte_array_set_size(buf, buf->len + sizeof(*ptr)); ptr = (void*)(buf->data + (buf->len - sizeof(*ptr))); *ptr = value; } static void gbinder_writer_data_overwrite_int32( GBinderWriterData* data, gsize offset, gint32 value) { GByteArray* buf = data->bytes; if (buf->len >= offset + sizeof(gint32)) { *((gint32*)(buf->data + offset)) = value; } else { GWARN("Can't overwrite at %lu as buffer is only %u bytes long", (gulong)offset, buf->len); } } void gbinder_writer_overwrite_int32( GBinderWriter* self, gsize offset, gint32 value) /* since 1.0.21 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_overwrite_int32(data, offset, value); } } void gbinder_writer_append_int64( GBinderWriter* self, guint64 value) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_int64(data, value); } } void gbinder_writer_data_append_int64( GBinderWriterData* data, guint64 value) { GByteArray* buf = data->bytes; guint64* ptr; g_byte_array_set_size(buf, buf->len + sizeof(*ptr)); ptr = (void*)(buf->data + (buf->len - sizeof(*ptr))); *ptr = value; } void gbinder_writer_append_float( GBinderWriter* self, gfloat value) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_float(data, value); } } void gbinder_writer_data_append_float( GBinderWriterData* data, gfloat value) { GByteArray* buf = data->bytes; gfloat* ptr; g_byte_array_set_size(buf, buf->len + sizeof(*ptr)); ptr = (void*)(buf->data + (buf->len - sizeof(*ptr))); *ptr = value; } void gbinder_writer_append_double( GBinderWriter* self, gdouble value) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_double(data, value); } } void gbinder_writer_data_append_double( GBinderWriterData* data, gdouble value) { GByteArray* buf = data->bytes; gdouble* ptr; g_byte_array_set_size(buf, buf->len + sizeof(*ptr)); ptr = (void*)(buf->data + (buf->len - sizeof(*ptr))); *ptr = value; } void gbinder_writer_append_string8( GBinderWriter* self, const char* str) { gbinder_writer_append_string8_len(self, str, str ? strlen(str) : 0); } void gbinder_writer_append_string8_len( GBinderWriter* self, const char* str, gsize len) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_string8_len(data, str, len); } } void gbinder_writer_data_append_string8( GBinderWriterData* data, const char* str) { gbinder_writer_data_append_string8_len(data, str, str ? strlen(str) : 0); } void gbinder_writer_data_append_string8_len( GBinderWriterData* data, const char* str, gsize len) { if (G_LIKELY(str)) { GByteArray* buf = data->bytes; const gsize old_size = buf->len; gsize padded_len = G_ALIGN4(len + 1); guint32* dest; /* Preallocate space */ g_byte_array_set_size(buf, old_size + padded_len); /* Zero the last word */ dest = (guint32*)(buf->data + old_size); dest[padded_len/4 - 1] = 0; /* Copy the data */ memcpy(dest, str, len); } } void gbinder_writer_append_string16( GBinderWriter* self, const char* utf8) { gbinder_writer_append_string16_len(self, utf8, utf8 ? strlen(utf8) : 0); } void gbinder_writer_append_string16_len( GBinderWriter* self, const char* utf8, gssize num_bytes) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_string16_len(data, utf8, num_bytes); } } void gbinder_writer_data_append_string16( GBinderWriterData* data, const char* utf8) { gbinder_writer_data_append_string16_len(data, utf8, utf8? strlen(utf8) : 0); } static void gbinder_writer_data_append_string16_null( GBinderWriterData* data) { /* NULL string */ gbinder_writer_data_append_int32(data, -1); } static void gbinder_writer_data_append_string16_empty( GBinderWriterData* data) { GByteArray* buf = data->bytes; const gsize old_size = buf->len; guint16* ptr16; /* Empty string */ g_byte_array_set_size(buf, old_size + 8); ptr16 = (guint16*)(buf->data + old_size); ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff; } void gbinder_writer_data_append_string16_len( GBinderWriterData* data, const char* utf8, gssize num_bytes) { if (utf8) { const char* end = utf8; g_utf8_validate(utf8, num_bytes, &end); num_bytes = end - utf8; } else { num_bytes = 0; } if (num_bytes > 0) { GByteArray* buf = data->bytes; const gsize old_size = buf->len; glong len = g_utf8_strlen(utf8, num_bytes); gsize padded_len = G_ALIGN4((len+1)*2); guint32* len_ptr; gunichar2* utf16_ptr; gunichar2* utf16 = NULL; /* Create utf-16 string to make sure of its size */ if (len > 0) { glong utf16_len = 0; utf16 = g_utf8_to_utf16(utf8, num_bytes, NULL, &utf16_len, NULL); if (utf16) { len = utf16_len; padded_len = G_ALIGN4((len+1)*2); } } /* Preallocate space */ g_byte_array_set_size(buf, old_size + padded_len + 4); len_ptr = (guint32*)(buf->data + old_size); utf16_ptr = (gunichar2*)(len_ptr + 1); /* Copy string */ if (utf16) { memcpy(utf16_ptr, utf16, len*2); g_free(utf16); } /* Actual length */ *len_ptr = len; /* Zero padding */ if (padded_len > len*2) { memset(utf16_ptr + len, 0, padded_len - len*2); } } else if (utf8) { /* Empty string */ gbinder_writer_data_append_string16_empty(data); } else { /* NULL string */ gbinder_writer_data_append_string16_null(data); } } static void gbinder_writer_data_append_string16_utf16( GBinderWriterData* data, const gunichar2* utf16, gssize length) { if (length < 0) { length = 0; if (utf16) { const guint16* ptr; /* Assume NULL terminated string */ for (ptr = utf16; *ptr; ptr++); length = ptr - utf16; } } if (length > 0) { GByteArray* buf = data->bytes; const gsize old_size = buf->len; const gsize padded_size = G_ALIGN4((length + 1) * 2); guint32* len_ptr; gunichar2* utf16_ptr; /* Preallocate space */ g_byte_array_set_size(buf, old_size + padded_size + 4); len_ptr = (guint32*)(buf->data + old_size); utf16_ptr = (gunichar2*)(len_ptr + 1); /* Actual length */ *len_ptr = length; /* Characters */ memcpy(utf16_ptr, utf16, 2 * length); /* Zero padding */ memset(utf16_ptr + length, 0, padded_size - 2 * length); } else if (utf16) { /* Empty string */ gbinder_writer_data_append_string16_empty(data); } else { /* NULL string */ gbinder_writer_data_append_string16_null(data); } } void gbinder_writer_append_string16_utf16( GBinderWriter* self, const gunichar2* utf16, gssize length) /* Since 1.0.17 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_string16_utf16(data, utf16, length); } } void gbinder_writer_append_bytes( GBinderWriter* self, const void* ptr, gsize size) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { g_byte_array_append(data->bytes, ptr, size); } } static void gbinder_writer_data_close_fd( gpointer data) { const int fd = GPOINTER_TO_INT(data); if (close(fd) < 0) { GWARN("Error closing fd %d: %s", fd, strerror(errno)); } } void gbinder_writer_data_append_fd( GBinderWriterData* data, int fd) { GByteArray* buf = data->bytes; const guint offset = buf->len; /* Duplicate the descriptor so that caller can do whatever with * the one it passed in. */ const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); guint written; /* Preallocate enough space */ g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE); /* Write the original fd if we failed to dup it */ if (dupfd < 0) { GWARN("Error dupping fd %d: %s", fd, strerror(errno)); written = data->io->encode_fd_object(buf->data + offset, fd); } else { written = data->io->encode_fd_object(buf->data + offset, dupfd); gbinder_writer_cleanup_add(data, gbinder_writer_data_close_fd, GINT_TO_POINTER(dupfd)); } /* Fix the data size */ g_byte_array_set_size(buf, offset + written); /* Record the offset */ gbinder_writer_data_record_offset(data, offset); } void gbinder_writer_append_fd( GBinderWriter* self, int fd) /* Since 1.0.18 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_fd(data, fd); } } void gbinder_writer_data_append_fda_object( GBinderWriterData* data, const GBinderFds *fds, const GBinderParent* parent) { GByteArray* buf = data->bytes; const guint offset = buf->len; guint written; /* Preallocate enough space */ g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE); written = data->io->encode_fda_object(buf->data + offset, fds, parent); /* Fix the data size */ g_byte_array_set_size(buf, offset + written); /* Record the offset */ gbinder_writer_data_record_offset(data, offset); } void gbinder_writer_data_append_fds( GBinderWriterData* data, const GBinderFds *fds, const GBinderParent* parent) { if (fds) { /* Size, fds data buffer and fd_array_object */ const gsize fds_total = sizeof(GBinderFds) + sizeof(int) * (fds->num_fds + fds->num_ints); GBinderParent fds_parent; gbinder_writer_data_append_int64(data, fds_total); fds_parent.index = gbinder_writer_data_append_buffer_object(data, fds, fds_total, parent); fds_parent.offset = sizeof(GBinderFds); gbinder_writer_data_append_fda_object(data, fds, &fds_parent); } else { /* If the pointer is null only write zero size */ gbinder_writer_data_append_int64(data, 0); } } void gbinder_writer_append_fds( GBinderWriter* self, const GBinderFds *fds, const GBinderParent* parent) /* Since 1.0.43 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_fds(data, fds, parent); } } guint gbinder_writer_append_buffer_object_with_parent( GBinderWriter* self, const void* buf, gsize len, const GBinderParent* parent) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { return gbinder_writer_data_append_buffer_object(data, buf, len, parent); } return 0; } guint gbinder_writer_append_buffer_object( GBinderWriter* self, const void* buf, gsize len) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { return gbinder_writer_data_append_buffer_object(data, buf, len, NULL); } return 0; } static void gbinder_writer_append_fields( GBinderWriter* writer, const void* obj, const GBinderWriterField* fields, const GBinderParent* parent) /* Since 1.1.27 */ { if (fields) { const GBinderWriterField* field = fields; const guint8* base_ptr = obj; GBinderParent parent2; parent2.index = parent->index; for (field = fields; field->type || field->write_buf; field++) { const void* field_ptr = base_ptr + field->offset; parent2.offset = parent->offset + field->offset; if (field->write_buf) { field->write_buf(writer, field_ptr, field, &parent2); } else { gbinder_writer_append_buffer_object_with_parent(writer, *(void**)field_ptr, field->type->size, &parent2); } } } } /* * Note that gbinder_writer_append_struct doesn't copy the data, it writes * buffer objects pointing to whatever was passed in. The caller must make * sure that those pointers outlive the transaction. That's most commonly * done with by using gbinder_writer_malloc() and friends for allocating * memory for the transaction. */ void gbinder_writer_append_struct( GBinderWriter* writer, const void* ptr, const GBinderWriterType* type, const GBinderParent* parent) /* Since 1.1.27 */ { if (type) { GBinderParent child; child.offset = 0; child.index = gbinder_writer_append_buffer_object_with_parent(writer, ptr, type->size, parent); gbinder_writer_append_fields(writer, ptr, type->fields, &child); } else { /* No type - no fields */ gbinder_writer_append_buffer_object_with_parent(writer, ptr, 0, parent); } } /* * Appends top-level vec. Allocates GBinderHidlVec for that, but * unlike gbinder_writer_append_hidl_vec(), doesn't copy the contents * of the vector. */ void gbinder_writer_append_struct_vec( GBinderWriter* writer, const void* ptr, guint count, const GBinderWriterType* type) /* Since 1.1.29 */ { GBinderHidlVec* vec = gbinder_writer_new0(writer, GBinderHidlVec); GBinderWriterField vec_f[2]; GBinderWriterType vec_t; memset(vec_f, 0, sizeof(vec_f)); vec_f->name = "hidl_vec.data.ptr"; vec_f->type = type; vec_f->write_buf = gbinder_writer_field_hidl_vec_write_buf; memset(&vec_t, 0, sizeof(vec_t)); vec_t.name = "hidl_vec"; vec_t.size = sizeof(GBinderHidlVec); vec_t.fields = vec_f; vec->owns_buffer = TRUE; vec->data.ptr = ptr; vec->count = count; gbinder_writer_append_struct(writer, vec, &vec_t, NULL); } void gbinder_writer_field_hidl_vec_write_buf( GBinderWriter* writer, const void* ptr, const GBinderWriterField* field, const GBinderParent* parent) /* Since 1.1.27 */ { const GBinderHidlVec* vec = ptr; const guint8* buf = vec->data.ptr; const GBinderWriterType* elem_type = field->type; if (elem_type) { GBinderParent child; guint i; child.index = gbinder_writer_append_buffer_object_with_parent (writer, buf, vec->count * elem_type->size, parent); for (i = 0; i < vec->count; i++) { child.offset = elem_type->size * i; gbinder_writer_append_fields(writer, buf + child.offset, elem_type->fields, &child); } } else { /* Probably a programming error but write an empty buffer anyway */ gbinder_writer_append_buffer_object_with_parent(writer, buf, 0, parent); } } void gbinder_writer_field_hidl_string_write_buf( GBinderWriter* writer, const void* ptr, const GBinderWriterField* field, const GBinderParent* parent) /* Since 1.1.27 */ { const GBinderHidlString* str = ptr; gbinder_writer_append_buffer_object_with_parent(writer, str->data.str, str->data.str ? (str->len + 1) : 0, parent); } guint gbinder_writer_data_append_buffer_object( GBinderWriterData* data, const void* ptr, gsize size, const GBinderParent* parent) { const guint index = data->offsets ? data->offsets->count : 0; GByteArray* buf = data->bytes; const guint offset = buf->len; guint n; /* Preallocate enough space */ g_byte_array_set_size(buf, offset + GBINDER_MAX_BUFFER_OBJECT_SIZE); /* Write the object */ n = data->io->encode_buffer_object(buf->data + offset, ptr, size, parent); /* Fix the data size */ g_byte_array_set_size(buf, offset + n); /* Record the offset */ gbinder_writer_data_record_offset(data, offset); /* The driver seems to require each buffer to be 8-byte aligned */ data->buffers_size += G_ALIGN8(size); return index; } /* * This is supposed to be used to write aidl parcelables, and is not * guaranteed to work on any other kind of parcelable. */ void gbinder_writer_append_parcelable( GBinderWriter* self, const void* buf, gsize len) /* Since 1.1.19 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_parcelable(data, buf, len); } } /* * This is compatible with aidl parcelables, and is not guaranteed to work * with any other kind of parcelable. */ void gbinder_writer_data_append_parcelable( GBinderWriterData* data, const void* ptr, gsize size) { if (ptr) { /* Non-null */ gbinder_writer_data_append_int32(data, kNonNullParcelableFlag); /* * Write the parcelable size, taking in account the size of this * integer as well. */ gbinder_writer_data_append_int32(data, size + sizeof(gint32)); /* Append the parcelable data */ g_byte_array_append(data->bytes, ptr, size); } else { /* Null */ gbinder_writer_data_append_int32(data, kNullParcelableFlag); } } void gbinder_writer_start_parcelable( GBinderWriter* self, GBinderWriter* parcelable) /* Since 1.1.47 */ { GBinderWriterData* data = gbinder_writer_data(self); /* Non-null parcelable is assumed */ gbinder_writer_data_append_int32(data, kNonNullParcelableFlag); gbinder_writer_init(parcelable, data, data->bytes->len); /* * Write the dummy parcelable size which is later fixed by * gbinder_writer_finish_parcelable. */ gbinder_writer_data_append_int32(data, 0); } void gbinder_writer_finish_parcelable( GBinderWriter* parcelable) /* Since 1.1.47 */ { GBinderWriterPriv* priv = gbinder_writer_cast(parcelable); gbinder_writer_data_overwrite_int32(priv->data, priv->offset, priv->data->bytes->len - priv->offset); } void gbinder_writer_append_hidl_string( GBinderWriter* self, const char* str) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_hidl_string(data, str); } } void gbinder_writer_append_hidl_string_copy( GBinderWriter* self, const char* str) /* Since 1.1.13 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { static const char empty[] = ""; gbinder_writer_data_append_hidl_string(data, str ? (str[0] ? gbinder_writer_strdup(self, str) : empty) : NULL); } } void gbinder_writer_data_append_hidl_vec( GBinderWriterData* data, const void* base, guint count, guint elemsize) { GBinderParent vec_parent; GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1); const gsize total = count * elemsize; void* buf = gutil_memdup(base, total); /* Fill in the vector descriptor */ if (buf) { vec->data.ptr = buf; vec->count = count; gbinder_writer_cleanup_add_ptr(data, buf); } vec->owns_buffer = TRUE; gbinder_writer_cleanup_add_ptr(data, vec); /* Every vector, even the one without data, requires two buffer objects */ vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET; vec_parent.index = gbinder_writer_data_append_buffer_object(data, vec, sizeof(*vec), NULL); gbinder_writer_data_append_buffer_object(data, buf, total, &vec_parent); } void gbinder_writer_append_hidl_vec( GBinderWriter* self, const void* base, guint count, guint elemsize) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_hidl_vec(data, base, count, elemsize); } } void gbinder_writer_data_append_hidl_string( GBinderWriterData* data, const char* str) { GBinderParent str_parent; GBinderHidlString* hidl_string = g_new0(GBinderHidlString, 1); const gsize len = str ? strlen(str) : 0; /* Fill in the string descriptor and store it */ hidl_string->data.str = str; hidl_string->len = len; hidl_string->owns_buffer = TRUE; gbinder_writer_cleanup_add_ptr(data, hidl_string); /* Write the buffer object pointing to the string descriptor */ str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET; str_parent.index = gbinder_writer_data_append_buffer_object(data, hidl_string, sizeof(*hidl_string), NULL); if (str) { /* Write the buffer pointing to the string data including the * NULL terminator, referencing string descriptor as a parent. */ gbinder_writer_data_append_buffer_object(data, str, len+1, &str_parent); GVERBOSE_("\"%s\" %u %u %u", str, (guint)len, (guint)str_parent.index, (guint)data->buffers_size); } else { gbinder_writer_data_append_buffer_object(data, NULL, 0, &str_parent); } } void gbinder_writer_append_hidl_string_vec( GBinderWriter* self, const char* strv[], gssize count) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_hidl_string_vec(data, strv, count); } } void gbinder_writer_data_append_hidl_string_vec( GBinderWriterData* data, const char* strv[], gssize count) { GBinderParent vec_parent; GBinderHidlVec* vec = g_new0(GBinderHidlVec, 1); GBinderHidlString* strings = NULL; int i; if (count < 0) { /* Assume NULL terminated array */ count = gutil_strv_length((char**)strv); } /* Fill in the vector descriptor */ if (count > 0) { strings = g_new0(GBinderHidlString, count); vec->data.ptr = strings; gbinder_writer_cleanup_add_ptr(data, strings); } vec->count = count; vec->owns_buffer = TRUE; gbinder_writer_cleanup_add_ptr(data, vec); /* Fill in string descriptors */ for (i = 0; i < count; i++) { const char* str = strv[i]; GBinderHidlString* hidl_str = strings + i; if ((hidl_str->data.str = str) != NULL) { hidl_str->len = strlen(str); hidl_str->owns_buffer = TRUE; } } /* Write the vector object */ vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET; vec_parent.index = gbinder_writer_data_append_buffer_object(data, vec, sizeof(*vec), NULL); if (strings) { GBinderParent str_parent; /* Prepare parent descriptor for the string data */ str_parent.index = data->offsets->count; str_parent.offset = GBINDER_HIDL_STRING_BUFFER_OFFSET; /* Write the vector data (it's parent for the string data) */ gbinder_writer_data_append_buffer_object(data, strings, sizeof(*strings) * count, &vec_parent); /* Write the string data */ for (i = 0; i < count; i++) { GBinderHidlString* hidl_str = strings + i; if (hidl_str->data.str) { gbinder_writer_data_append_buffer_object(data, hidl_str->data.str, hidl_str->len + 1, &str_parent); GVERBOSE_("%d. \"%s\" %u %u %u", i + 1, hidl_str->data.str, (guint)hidl_str->len, (guint)str_parent.index, (guint)data->buffers_size); } else { GVERBOSE_("%d. NULL %u %u %u", i + 1, (guint)hidl_str->len, (guint)str_parent.index, (guint)data->buffers_size); gbinder_writer_data_append_buffer_object(data, NULL, 0, &str_parent); } str_parent.offset += sizeof(GBinderHidlString); } } else { gbinder_writer_data_append_buffer_object(data, NULL, 0, &vec_parent); } } void gbinder_writer_append_local_object( GBinderWriter* self, GBinderLocalObject* obj) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_local_object(data, obj); } } void gbinder_writer_data_append_local_object( GBinderWriterData* data, GBinderLocalObject* obj) { GByteArray* buf = data->bytes; const guint offset = buf->len; guint n; /* Preallocate enough space */ g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE); /* Write the object */ n = data->io->encode_local_object(buf->data + offset, obj, data->protocol); /* Fix the data size */ g_byte_array_set_size(buf, offset + n); if (obj) { /* Record the offset */ gbinder_writer_data_record_offset(data, offset); } } void gbinder_writer_append_remote_object( GBinderWriter* self, GBinderRemoteObject* obj) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_data_append_remote_object(data, obj); } } void gbinder_writer_append_byte_array( GBinderWriter* self, const void* byte_array, gint32 len) /* since 1.0.12 */ { GBinderWriterData* data = gbinder_writer_data(self); GASSERT(len >= 0); if (G_LIKELY(data)) { GByteArray* buf = data->bytes; gsize padded_len; guint8* ptr; if (!byte_array) { len = 0; } /* * Android aligns byte array reads and writes to 4 bytes and * pads with 0xFF. */ padded_len = G_ALIGN4(len); g_byte_array_set_size(buf, buf->len + sizeof(len) + padded_len); ptr = buf->data + (buf->len - sizeof(len) - padded_len); if (len > 0) { *((gint32*)ptr) = len; ptr += sizeof(len); memcpy(ptr, byte_array, len); /* FF padding */ if (padded_len > len) { memset(ptr + len, 0xff, padded_len - len); } } else { *((gint32*)ptr) = -1; } } } #if GBINDER_FMQ_SUPPORTED void gbinder_writer_append_fmq_descriptor( GBinderWriter* self, const GBinderFmq* queue) /* since 1.1.14 */ { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data) && G_LIKELY(queue)) { data->protocol->write_fmq_descriptor(self, queue); } } #endif /* GBINDER_FMQ_SUPPORTED */ void gbinder_writer_data_append_remote_object( GBinderWriterData* data, GBinderRemoteObject* obj) { GByteArray* buf = data->bytes; const guint offset = buf->len; guint n; /* Preallocate enough space */ g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE); /* Write the object */ n = data->io->encode_remote_object(buf->data + offset, obj, data->protocol); /* Fix the data size */ g_byte_array_set_size(buf, offset + n); if (obj) { /* Record the offset */ gbinder_writer_data_record_offset(data, offset); } } static void* gbinder_writer_alloc( GBinderWriter* self, gsize size, gpointer (*alloc)(gsize), void (*dealloc)(gpointer)) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { return gbinder_writer_cleanup_add(data, dealloc, alloc(size)); } return NULL; } void* gbinder_writer_malloc( GBinderWriter* self, gsize size) /* since 1.0.19 */ { return gbinder_writer_alloc(self, size, g_malloc, g_free); } void* gbinder_writer_malloc0( GBinderWriter* self, gsize size) /* since 1.0.19 */ { return gbinder_writer_alloc(self, size, g_malloc0, g_free); } char* gbinder_writer_strdup( GBinderWriter* writer, const char* str) /* since 1.1.13 */ { return str ? gbinder_writer_memdup(writer, str, strlen(str) + 1) : NULL; } void* gbinder_writer_memdup( GBinderWriter* self, const void* buf, gsize size) /* since 1.0.19 */ { if (buf) { void* ptr = gbinder_writer_malloc(self, size); if (ptr) { memcpy(ptr, buf, size); return ptr; } } return NULL; } void gbinder_writer_add_cleanup( GBinderWriter* self, GDestroyNotify destroy, gpointer ptr) /* since 1.0.19 */ { if (G_LIKELY(destroy)) { GBinderWriterData* data = gbinder_writer_data(self); if (G_LIKELY(data)) { gbinder_writer_cleanup_add(data, destroy, ptr); } } } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/src/gbinder_writer_p.h000066400000000000000000000113271526127616700202110ustar00rootroot00000000000000/* * Copyright (C) 2025-2026 Jolla Mobile Ltd * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GBINDER_WRITER_PRIVATE_H #define GBINDER_WRITER_PRIVATE_H #include #include "gbinder_types_p.h" typedef struct gbinder_writer_data { const GBinderIo* io; const GBinderRpcProtocol* protocol; GByteArray* bytes; GUtilIntArray* offsets; gsize buffers_size; GUtilCleanup* cleanup; } GBinderWriterData; void gbinder_writer_init( GBinderWriter* writer, GBinderWriterData* data, guint offset) GBINDER_INTERNAL; void gbinder_writer_data_set_contents( GBinderWriterData* data, GBinderBuffer* buffer, GBinderObjectConverter* convert) GBINDER_INTERNAL; void gbinder_writer_data_append_contents( GBinderWriterData* data, GBinderBuffer* buffer, gsize data_offset, GBinderObjectConverter* convert) GBINDER_INTERNAL; void gbinder_writer_data_append_bool( GBinderWriterData* data, gboolean value) GBINDER_INTERNAL; void gbinder_writer_data_append_int32( GBinderWriterData* data, guint32 value) GBINDER_INTERNAL; void gbinder_writer_data_append_int64( GBinderWriterData* data, guint64 value) GBINDER_INTERNAL; void gbinder_writer_data_append_float( GBinderWriterData* data, gfloat value) GBINDER_INTERNAL; void gbinder_writer_data_append_double( GBinderWriterData* data, gdouble value) GBINDER_INTERNAL; void gbinder_writer_data_append_string8( GBinderWriterData* data, const char* str) GBINDER_INTERNAL; void gbinder_writer_data_append_string8_len( GBinderWriterData* data, const char* str, gsize len) GBINDER_INTERNAL; void gbinder_writer_data_append_string16( GBinderWriterData* data, const char* utf8) GBINDER_INTERNAL; void gbinder_writer_data_append_string16_len( GBinderWriterData* data, const char* utf8, gssize num_bytes) GBINDER_INTERNAL; guint gbinder_writer_data_append_buffer_object( GBinderWriterData* data, const void* ptr, gsize size, const GBinderParent* parent) GBINDER_INTERNAL; void gbinder_writer_data_append_parcelable( GBinderWriterData* data, const void* ptr, gsize size) GBINDER_INTERNAL; void gbinder_writer_data_append_hidl_vec( GBinderWriterData* data, const void* base, guint count, guint elemsize) GBINDER_INTERNAL; void gbinder_writer_data_append_hidl_string( GBinderWriterData* data, const char* str) GBINDER_INTERNAL; void gbinder_writer_data_append_hidl_string_vec( GBinderWriterData* data, const char* strv[], gssize count) GBINDER_INTERNAL; void gbinder_writer_data_append_local_object( GBinderWriterData* data, GBinderLocalObject* obj) GBINDER_INTERNAL; void gbinder_writer_data_append_remote_object( GBinderWriterData* data, GBinderRemoteObject* obj) GBINDER_INTERNAL; void gbinder_writer_data_append_fd( GBinderWriterData* data, int fd) GBINDER_INTERNAL; #endif /* GBINDER_WRITER_PRIVATE_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/000077500000000000000000000000001526127616700146775ustar00rootroot00000000000000libgbinder-1.1.54/test/Makefile000066400000000000000000000002771526127616700163450ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- all: %: @$(MAKE) -C binder-client $* @$(MAKE) -C binder-dump $* @$(MAKE) -C binder-service $* @$(MAKE) -C binder-watch $* @$(MAKE) -C rild-card-status $* libgbinder-1.1.54/test/README000066400000000000000000000001171526127616700155560ustar00rootroot00000000000000libdbinder test applications and examples, primarily for development purposes. libgbinder-1.1.54/test/ashmem-test/000077500000000000000000000000001526127616700171265ustar00rootroot00000000000000libgbinder-1.1.54/test/ashmem-test/Makefile000066400000000000000000000001161526127616700205640ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = ashmem-test include ../common/Makefile libgbinder-1.1.54/test/ashmem-test/ashmem-test.c000066400000000000000000000162741526127616700215330ustar00rootroot00000000000000/* * Copyright (C) 2021 Jolla Ltd. * Copyright (C) 2021 Slava Monich * * 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 #include #include #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEFAULT_BINDER GBINDER_DEFAULT_HWBINDER #define ALLOCATOR_IFACE "android.hidl.allocator@1.0::IAllocator" #define DEFAULT_FQNAME ALLOCATOR_IFACE "/ashmem" #define TX_ALLOCATE GBINDER_FIRST_CALL_TRANSACTION typedef struct app_options { const char* fqname; char* dev; gsize size; } AppOptions; static void app_dumpmem( const GBinderHidlMemory* mem) { const GBinderFds* fds = mem->data.fds; GDEBUG("Name: %s", mem->name.data.str); GDEBUG("Size: %" G_GUINT64_FORMAT " bytes", mem->size); GASSERT(fds->version == GBINDER_HIDL_FDS_VERSION); GDEBUG("Contains %u fd(s)", fds->num_fds); if (fds->num_fds) { guint i; for (i = 0; i < fds->num_fds; i++) { int fd = gbinder_fds_get_fd(fds, i); guint8* ptr = mmap(NULL, mem->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr) { gsize off = 0; GDEBUG("fd %d => %p", fd, ptr); while (off < mem->size) { char line[GUTIL_HEXDUMP_BUFSIZE]; guint n = gutil_hexdump(line, ptr + off, mem->size - off); GDEBUG("%04X: %s", (uint) off, line); off += n; } munmap(ptr, mem->size); } else { GDEBUG("fd %d", fd); } } } } static int app_allocate( const AppOptions* opt, GBinderClient* client) { GBinderLocalRequest* request = gbinder_client_new_request(client); GBinderRemoteReply* reply; int status, ret; gbinder_local_request_append_int64(request, opt->size); reply = gbinder_client_transact_sync_reply(client, TX_ALLOCATE, request, &status); if (reply) { GBinderReader reader; gint32 tx_status; gboolean success; gbinder_remote_reply_init_reader(reply, &reader); if (gbinder_reader_read_int32(&reader, &tx_status) && gbinder_reader_read_bool(&reader, &success) && tx_status == GBINDER_STATUS_OK && success) { const GBinderHidlMemory* mem = gbinder_reader_read_hidl_struct (&reader, GBinderHidlMemory); if (mem) { GINFO("OK"); app_dumpmem(mem); } else { GINFO("OOPS"); } } else { GINFO("FAILED"); } ret = RET_OK; } else { GERR("Call failed (%d)", status); ret = RET_ERR; } gbinder_local_request_unref(request); gbinder_remote_reply_unref(reply); return ret; } static int app_run( const AppOptions* opt) { int ret = RET_NOTFOUND; GBinderServiceManager* sm = gbinder_servicemanager_new(opt->dev); if (sm) { int status = 0; GBinderRemoteObject* remote = gbinder_servicemanager_get_service_sync (sm, opt->fqname, &status); if (remote) { GBinderClient* client = gbinder_client_new(remote, ALLOCATOR_IFACE); ret = app_allocate(opt, client); gbinder_client_unref(client); } else { GERR("%s not found", opt->fqname); } gbinder_servicemanager_unref(sm); } else { GERR("No servicemanager at %s", opt->dev); } return ret; } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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_NONE; 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_BINDER "]", "DEVICE" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[FQNAME]"); gutil_log_timestamp = FALSE; gutil_log_default.level = GLOG_LEVEL_DEFAULT; g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { if (!opt->dev || !opt->dev[0]) { opt->dev = g_strdup(DEFAULT_BINDER); } if (argc < 3) { opt->fqname = ((argc == 2) ? argv[1] : DEFAULT_FQNAME); opt->size = 64; ok = TRUE; } else { char* 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[]) { AppOptions opt; int ret = RET_INVARG; memset(&opt, 0, sizeof(opt)); if (app_init(&opt, argc, argv)) { ret = app_run(&opt); } g_free(opt.dev); return ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/binder-client/000077500000000000000000000000001526127616700174165ustar00rootroot00000000000000libgbinder-1.1.54/test/binder-client/Makefile000066400000000000000000000001201526127616700210470ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-client include ../common/Makefile libgbinder-1.1.54/test/binder-client/binder-client.c000066400000000000000000000226251526127616700223100ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 #include #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEFAULT_DEVICE "/dev/binder" #define DEFAULT_NAME "test" #define DEFAULT_IFACE "test@1.0" typedef struct app_options { char* dev; char* iface; char* fqname; const char* name; } AppOptions; typedef struct app { const AppOptions* opt; char* fqname; GMainLoop* loop; GBinderServiceManager* sm; GBinderLocalObject* local; GBinderRemoteObject* remote; gulong wait_id; gulong death_id; GBinderClient* client; GThread* thread; int ret; } App; typedef struct app_input { App* app; char* str; } AppInput; static const char pname[] = "binder-client"; static gboolean app_signal( gpointer user_data) { App* app = user_data; GINFO("Caught signal, shutting down..."); g_main_loop_quit(app->loop); return G_SOURCE_CONTINUE; } static void app_remote_died( GBinderRemoteObject* obj, void* user_data) { App* app = user_data; GINFO("Remote has died, exiting..."); g_main_loop_quit(app->loop); } static void app_call( App* app, char* str) { GBinderLocalRequest* req = gbinder_client_new_request(app->client); GBinderRemoteReply* reply; int status; gbinder_local_request_append_string16(req, str); reply = gbinder_client_transact_sync_reply(app->client, GBINDER_FIRST_CALL_TRANSACTION, req, &status); gbinder_local_request_unref(req); if (status == GBINDER_STATUS_OK) { GBinderReader reader; char* ret; gbinder_remote_reply_init_reader(reply, &reader); ret = gbinder_reader_read_string16(&reader); GDEBUG_("Reply: \"%s\"", ret); g_free(ret); } else { GERR_("status %d", status); } gbinder_remote_reply_unref(reply); } static gboolean app_input( void* user_data) { AppInput* input = user_data; GDEBUG_("\"%s\"", input->str); app_call(input->app, input->str); g_free(input->str); g_free(input); return G_SOURCE_REMOVE; } static gpointer app_input_thread( gpointer data) { int c; App* app = data; GString* buf = g_string_new(""); while ((c = getc(stdin)) != EOF) { if (c == '\n' || c == '\r') { AppInput* input = g_new0(AppInput, 1); input->app = app; input->str = g_strdup(buf->str); g_idle_add(app_input, input); g_string_truncate(buf, 0); while (c == '\n' || c == '\r') c = getc(stdin); if (c == EOF) { break; } else { ungetc(c, stdin); continue; } } else { g_string_append_c(buf, (char)c); } } GDEBUG_("Input thread exiting..."); g_string_free(buf, TRUE); return NULL; } static gboolean app_connect_remote( App* app) { app->remote = gbinder_servicemanager_get_service_sync(app->sm, app->fqname, NULL); /* autoreleased pointer */ if (app->remote) { const AppOptions* opt = app->opt; GINFO("Connected to %s", app->fqname); gbinder_remote_object_ref(app->remote); app->client = gbinder_client_new(app->remote, opt->iface); app->death_id = gbinder_remote_object_add_death_handler(app->remote, app_remote_died, app); app->thread = g_thread_new("input", app_input_thread, app); return TRUE; } return FALSE; } static void app_registration_handler( GBinderServiceManager* sm, const char* name, void* user_data) { App* app = user_data; GDEBUG("\"%s\" appeared", name); if (!strcmp(name, app->fqname) && app_connect_remote(app)) { gbinder_servicemanager_remove_handler(app->sm, app->wait_id); app->wait_id = 0; } } static void app_run( App* app) { const AppOptions* opt = app->opt; guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app); guint sigint = g_unix_signal_add(SIGINT, app_signal, app); app->fqname = opt->fqname ? g_strdup(opt->fqname) : strchr(opt->name, '/') ? g_strdup(opt->name) : g_strconcat(opt->iface, "/", opt->name, NULL); if (!app_connect_remote(app)) { GINFO("Waiting for %s", app->fqname); app->wait_id = gbinder_servicemanager_add_registration_handler(app->sm, app->fqname, app_registration_handler, app); } app->loop = g_main_loop_new(NULL, TRUE); app->ret = RET_OK; g_main_loop_run(app->loop); g_source_remove(sigtrm); g_source_remove(sigint); g_main_loop_unref(app->loop); if (app->thread) { /* Not the cleanest of exits, just dropping the thread... */ g_thread_unref(app->thread); } gbinder_remote_object_remove_handler(app->remote, app->death_id); gbinder_remote_object_unref(app->remote); gbinder_local_object_drop(app->local); gbinder_client_unref(app->client); g_free(app->fqname); } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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" }, { "interface", 'i', 0, G_OPTION_ARG_STRING, &opt->iface, "Interface name [" DEFAULT_IFACE "]", "IFACE" }, { "fqname", 'n', 0, G_OPTION_ARG_STRING, &opt->fqname, "Fully qualified name [IFACE/NAME]", "FQNAME" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[NAME]"); memset(opt, 0, sizeof(*opt)); gutil_log_set_type(GLOG_TYPE_STDERR, pname); g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { char* help; if (!opt->dev || !opt->dev[0]) opt->dev = g_strdup(DEFAULT_DEVICE); if (!opt->iface || !opt->iface[0]) opt->iface = g_strdup(DEFAULT_IFACE); switch (argc) { case 2: opt->name = argv[1]; ok = TRUE; break; case 1: opt->name = DEFAULT_NAME; ok = TRUE; break; default: help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); break; } } 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.local = gbinder_servicemanager_new_local_object(app.sm, NULL, NULL, NULL); app_run(&app); gbinder_servicemanager_unref(app.sm); } } g_free(opt.fqname); g_free(opt.iface); g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/binder-dump/000077500000000000000000000000001526127616700171055ustar00rootroot00000000000000libgbinder-1.1.54/test/binder-dump/Makefile000066400000000000000000000001161526127616700205430ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-dump include ../common/Makefile libgbinder-1.1.54/test/binder-dump/binder-dump.c000066400000000000000000000145351526127616700214670ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Copyright (C) 2018 Slava Monich * * 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 #include #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEV_DEFAULT GBINDER_DEFAULT_BINDER #define GBINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4) #define GBINDER_DUMP_TRANSACTION GBINDER_TRANSACTION('D','M','P') typedef struct app_options { char* dev; const char* service; } AppOptions; typedef struct app { const AppOptions* opt; GMainLoop* loop; GBinderServiceManager* sm; int ret; } App; static const char pname[] = "binder-dump"; static gboolean app_dump_service( App* app, const char* service) { int status = 0; GBinderRemoteObject* obj = gbinder_servicemanager_get_service_sync(app->sm, service, &status); if (obj) { GBinderClient* client = gbinder_client_new(obj, NULL); GBinderLocalRequest* req = gbinder_client_new_request(client); GBinderRemoteReply* reply; GBinderWriter writer; gbinder_remote_object_ref(obj); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_fd(&writer, STDOUT_FILENO); gbinder_writer_append_int32(&writer, 0); reply = gbinder_client_transact_sync_reply(client, GBINDER_DUMP_TRANSACTION, req, &status); if (status < 0) { GERR("Error %d", status); } gbinder_remote_object_unref(obj); gbinder_remote_reply_unref(reply); gbinder_local_request_unref(req); gbinder_client_unref(client); return TRUE; } else { GERR("No such service: %s (%d)", service, status); return FALSE; } } static void app_dump_services( App* app, char** strv) { if (strv) { while (*strv) { const char* name = *strv++; printf("========= %s\n", name); app_dump_service(app, name); } } } static void app_run( App* app) { const AppOptions* opt = app->opt; if (opt->service) { app->ret = app_dump_service(app, opt->service) ? RET_OK : RET_NOTFOUND; } else { char** services = gbinder_servicemanager_list_sync(app->sm); if (services) { app_dump_services(app, services); g_strfreev(services); app->ret = RET_OK; } else { app->ret = RET_ERR; } } } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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 [" DEV_DEFAULT "]", "DEVICE" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[SERVICE]"); memset(opt, 0, sizeof(*opt)); gutil_log_timestamp = FALSE; gutil_log_set_type(GLOG_TYPE_STDERR, pname); gutil_log_default.level = GLOG_LEVEL_DEFAULT; g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { char* help; if (!opt->dev || !opt->dev[0]) opt->dev = g_strdup(DEV_DEFAULT); switch (argc) { case 2: opt->service = argv[1]; /* no break */ case 1: ok = TRUE; break; default: help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); break; } } 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); } } g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/binder-service/000077500000000000000000000000001526127616700176005ustar00rootroot00000000000000libgbinder-1.1.54/test/binder-service/Makefile000066400000000000000000000001211526127616700212320ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-service include ../common/Makefile libgbinder-1.1.54/test/binder-service/binder-service.c000066400000000000000000000222631526127616700226520ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Jolla Ltd. * Copyright (C) 2018-2019 Slava Monich * * 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 #include #include #define BINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4) #define BINDER_DUMP_TRANSACTION BINDER_TRANSACTION('D','M','P') #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEFAULT_DEVICE "/dev/binder" #define DEFAULT_NAME "test" #define DEFAULT_IFACE "test@1.0" typedef struct app_options { char* dev; char* iface; const char* name; gboolean async; } AppOptions; typedef struct app { const AppOptions* opt; GMainLoop* loop; GBinderServiceManager* sm; GBinderLocalObject* obj; int ret; } App; typedef struct response { GBinderRemoteRequest* req; GBinderLocalReply* reply; } Response; static const char pname[] = "binder-service"; static gboolean app_signal( gpointer user_data) { App* app = user_data; GINFO("Caught signal, shutting down..."); g_main_loop_quit(app->loop); return G_SOURCE_CONTINUE; } static gboolean app_async_resp( gpointer user_data) { Response* resp = user_data; gbinder_remote_request_complete(resp->req, resp->reply, 0); return G_SOURCE_REMOVE; } static void app_async_free( gpointer user_data) { Response* resp = user_data; gbinder_local_reply_unref(resp->reply); gbinder_remote_request_unref(resp->req); g_free(resp); } static GBinderLocalReply* app_reply( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { App* app = user_data; GBinderReader reader; gbinder_remote_request_init_reader(req, &reader); if (code == GBINDER_FIRST_CALL_TRANSACTION) { const AppOptions* opt = app->opt; const char* iface = gbinder_remote_request_interface(req); if (!g_strcmp0(iface, opt->iface)) { char* str = gbinder_reader_read_string16(&reader); GBinderLocalReply* reply = gbinder_local_object_new_reply(obj); GVERBOSE("\"%s\" %u", iface, code); GDEBUG("\"%s\"", str); *status = 0; gbinder_local_reply_append_string16(reply, str); g_free(str); if (opt->async) { Response* resp = g_new0(Response, 1); resp->reply = reply; resp->req = gbinder_remote_request_ref(req); g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, app_async_resp, resp, app_async_free); gbinder_remote_request_block(resp->req); return NULL; } else { return reply; } } else { GDEBUG("Unexpected interface \"%s\"", iface); } } else if (code == BINDER_DUMP_TRANSACTION) { int fd = gbinder_reader_read_fd(&reader); const char* dump = "Sorry, I've got nothing to dump...\n"; const gssize dump_len = strlen(dump); GDEBUG("Dump request from %d", gbinder_remote_request_sender_pid(req)); if (write(fd, dump, dump_len) != dump_len) { GERR("Failed to write dump: %s", strerror(errno)); } *status = 0; return NULL; } *status = -1; return NULL; } static void app_add_service_done( GBinderServiceManager* sm, int status, void* user_data) { App* app = user_data; if (status == GBINDER_STATUS_OK) { printf("Added \"%s\"\n", app->opt->name); app->ret = RET_OK; } else { GERR("Failed to add \"%s\" (%d)", app->opt->name, status); g_main_loop_quit(app->loop); } } static void app_sm_presence_handler( GBinderServiceManager* sm, void* user_data) { App* app = user_data; if (gbinder_servicemanager_is_present(app->sm)) { GINFO("Service manager has reappeared"); gbinder_servicemanager_add_service(app->sm, app->opt->name, app->obj, app_add_service_done, app); } else { GINFO("Service manager has died"); } } static void app_run( App* app) { const char* name = app->opt->name; guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app); guint sigint = g_unix_signal_add(SIGINT, app_signal, app); gulong presence_id = gbinder_servicemanager_add_presence_handler (app->sm, app_sm_presence_handler, app); app->loop = g_main_loop_new(NULL, TRUE); gbinder_servicemanager_add_service(app->sm, name, app->obj, app_add_service_done, app); g_main_loop_run(app->loop); if (sigtrm) g_source_remove(sigtrm); if (sigint) g_source_remove(sigint); gbinder_servicemanager_remove_handler(app->sm, presence_id); g_main_loop_unref(app->loop); app->loop = NULL; } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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" }, { "interface", 'i', 0, G_OPTION_ARG_STRING, &opt->iface, "Local interface [" DEFAULT_IFACE "]", "IFACE" }, { "async", 'a', 0, G_OPTION_ARG_NONE, &opt->async, "Handle calls asynchronously", NULL }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[NAME]"); memset(opt, 0, sizeof(*opt)); gutil_log_timestamp = FALSE; gutil_log_set_type(GLOG_TYPE_STDERR, pname); gutil_log_default.level = GLOG_LEVEL_DEFAULT; g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { char* help; if (!opt->dev || !opt->dev[0]) opt->dev = g_strdup(DEFAULT_DEVICE); if (!opt->iface) opt->iface = g_strdup(DEFAULT_IFACE); switch (argc) { case 2: opt->name = argv[1]; ok = TRUE; break; case 1: opt->name = DEFAULT_NAME; ok = TRUE; break; default: help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); break; } } 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 (gbinder_servicemanager_wait(app.sm, -1)) { app.obj = gbinder_servicemanager_new_local_object (app.sm, opt.iface, app_reply, &app); app_run(&app); gbinder_local_object_unref(app.obj); gbinder_servicemanager_unref(app.sm); } } g_free(opt.iface); g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/binder-watch/000077500000000000000000000000001526127616700172465ustar00rootroot00000000000000libgbinder-1.1.54/test/binder-watch/Makefile000066400000000000000000000001171526127616700207050ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-watch include ../common/Makefile libgbinder-1.1.54/test/binder-watch/binder-watch.c000066400000000000000000000153141526127616700217650ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * * You may use this file under the terms of the 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 #include #include #define DEFAULT_DEVICE "/dev/binder" #define RET_OK (0) #define RET_ERR (1) #define RET_INVARG (2) typedef struct app_options { char* dev; const char* fqname; const char* suffix; gboolean exit_when_found; } AppOptions; typedef struct app { const AppOptions* opt; GMainLoop* loop; GBinderServiceManager* sm; GBinderRemoteObject* remote; gulong wait_id; gulong death_id; int ret; } App; static gboolean app_signal( gpointer user_data) { App* app = user_data; GINFO("Caught signal, shutting down..."); g_main_loop_quit(app->loop); return G_SOURCE_CONTINUE; } static void app_remote_drop( App* app) { gbinder_remote_object_remove_handler(app->remote, app->death_id); gbinder_remote_object_unref(app->remote); app->death_id = 0; app->remote = NULL; } static void app_remote_gone( GBinderRemoteObject* obj, void* user_data) { App* app = user_data; GINFO("Service %s is gone", app->opt->fqname); app_remote_drop(app); } static gboolean app_remote_attach( App* app) { app->remote = gbinder_servicemanager_get_service_sync(app->sm, app->opt->fqname, NULL); if (app->remote) { gbinder_remote_object_ref(app->remote); app->death_id = gbinder_remote_object_add_death_handler(app->remote, app_remote_gone, app); return TRUE; } else { return FALSE; } } static void app_registration_handler( GBinderServiceManager* sm, const char* name, void* user_data) { App* app = user_data; const AppOptions* opt = app->opt; if (!strcmp(name, opt->fqname)) { GINFO("%s appeared", name); if (opt->exit_when_found) { g_main_loop_quit(app->loop); app->ret = RET_OK; } else { app_remote_attach(app); } } else { GDEBUG("Ignoring %s", name); } } static void app_run( App* app) { const AppOptions* opt = app->opt; app->sm = gbinder_servicemanager_new(opt->dev); if (app_remote_attach(app)) { GINFO("Service %s is there", opt->fqname); } if (!app->remote || !opt->exit_when_found) { guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app); guint sigint = g_unix_signal_add(SIGINT, app_signal, app); GINFO("Watching for %s", opt->fqname); app->wait_id = gbinder_servicemanager_add_registration_handler(app->sm, opt->fqname, app_registration_handler, app); app->loop = g_main_loop_new(NULL, TRUE); app->ret = RET_OK; g_main_loop_run(app->loop); g_source_remove(sigtrm); g_source_remove(sigint); g_main_loop_unref(app->loop); gbinder_servicemanager_remove_handler(app->sm, app->wait_id); } app_remote_drop(app); gbinder_servicemanager_unref(app->sm); } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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[] = { { "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev, "Binder device [" DEFAULT_DEVICE "]", "DEVICE" }, { "wait", 'w', 0, G_OPTION_ARG_NONE, &opt->exit_when_found, "Wait until the service is registered and exit", NULL}, { "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 }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("FQNAME"); g_option_context_set_summary(options, "Tool for watching binder service " "names in servicemanager."); g_option_context_add_main_entries(options, entries, NULL); memset(opt, 0, sizeof(*opt)); if (g_option_context_parse(options, &argc, &argv, &error) && argc == 2 && argv[1][0]) { if (!opt->dev || !opt->dev[0]) { opt->dev = g_strdup(DEFAULT_DEVICE); } opt->fqname = argv[1]; ok = TRUE; } else if (error) { GERR("%s", error->message); g_error_free(error); } else { char* help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); ok = FALSE; } 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_run(&app); } g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/test/common/000077500000000000000000000000001526127616700161675ustar00rootroot00000000000000libgbinder-1.1.54/test/common/Makefile000066400000000000000000000061631526127616700176350ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- .PHONY: all debug release install clean cleaner .PHONY: libgbinder-release libgbinder-debug # # Executable must be defined # ifndef EXE ${error EXE not defined} endif # # Required packages # PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil # # Default target # all: debug release # # Sources # SRC ?= $(EXE).c # # Directories # SRC_DIR = . BUILD_DIR = build LIB_DIR = ../.. DEBUG_BUILD_DIR = $(BUILD_DIR)/debug RELEASE_BUILD_DIR = $(BUILD_DIR)/release # # Tools and flags # PKG_CONFIG ?= pkg-config CC ?= $(CROSS_COMPILE)gcc LD = $(CC) WARNINGS = -Wall DEFINES += -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32 \ -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_MAX_ALLOWED INCLUDES = -I$(LIB_DIR)/include CFLAGS += -fPIC $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \ $(shell $(PKG_CONFIG) --cflags $(PKGS)) LDFLAGS += -pie $(shell $(PKG_CONFIG) --libs $(PKGS)) QUIET_MAKE = $(MAKE) --no-print-directory DEBUG_FLAGS = -g RELEASE_FLAGS = KEEP_SYMBOLS ?= 0 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 = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) RELEASE_OBJS = $(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) # # Dependencies # DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) ifneq ($(MAKECMDGOALS),clean) ifneq ($(strip $(DEPS)),) -include $(DEPS) endif endif $(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 $@ $(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 $@ libgbinder-1.1.54/test/rild-card-status/000077500000000000000000000000001526127616700200615ustar00rootroot00000000000000libgbinder-1.1.54/test/rild-card-status/Makefile000066400000000000000000000001231526127616700215150ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = rild-card-status include ../common/Makefile libgbinder-1.1.54/test/rild-card-status/rild-card-status.c000066400000000000000000000307571526127616700234230ustar00rootroot00000000000000/* * Copyright (C) 2018 Jolla Ltd. * Contact: Slava Monich * * 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 name of Jolla Ltd nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * 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 #include #include #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEFAULT_DEVICE "/dev/hwbinder" #define DEFAULT_NAME "slot1" #define IFACE_RADIO "android.hardware.radio@1.0::IRadio" #define IFACE_RESPONSE "android.hardware.radio@1.0::IRadioResponse" #define IFACE_INDICATION "android.hardware.radio@1.0::IRadioIndication" /* android.hardware.radio@1.0::IRadio */ #define REQ_RADIO_SET_RESPONSE_FUNCTIONS (1) #define REQ_RADIO_GET_ICC_CARD_STATUS (2) /* android.hardware.radio@1.0::IRadioResponse */ #define RESP_GET_ICC_CARD_STATUS_RESPONSE (1) typedef struct app_options { char* dev; char* fqname; const char* name; } AppOptions; typedef struct app { const AppOptions* opt; GMainLoop* loop; GBinderServiceManager* sm; GBinderLocalObject* response; GBinderLocalObject* indication; int ret; } App; typedef struct radio_response_info { guint32 type; guint32 serial; guint32 error; } RadioResponseInfo; typedef struct radio_string { union { guint64 value; const char* str; } data; guint32 len; guint32 owns_buffer; } RadioString; typedef struct radio_app_status { guint32 appType; guint32 appState; guint32 persoSubstate; guint32 unused1; RadioString aid; RadioString label; guint32 pinReplaced; guint32 pin1; guint32 pin2; guint32 unused2; } RadioAppStatus; typedef struct radio_card_status { guint32 cardState; guint32 universalPinState; guint32 gsmUmtsSubscriptionAppIndex; guint32 cdmaSubscriptionAppIndex; guint32 imsSubscriptionAppIndex; guint32 unused1; union { guint64 value; const RadioAppStatus* array; } apps; guint32 numApps; guint32 unused2; } RadioCardStatus; static const char pname[] = "binder-client"; static gboolean app_signal( gpointer user_data) { App* app = user_data; GINFO("Caught signal, shutting down..."); g_main_loop_quit(app->loop); return G_SOURCE_CONTINUE; } static void app_remote_died( GBinderRemoteObject* obj, void* user_data) { App* app = user_data; GINFO("Remote has died, exiting..."); g_main_loop_quit(app->loop); } static void app_dump( const void* buf, gsize len) { const guint8* ptr = buf; while (len > 0) { char line[GUTIL_HEXDUMP_BUFSIZE]; const guint consumed = gutil_hexdump(line, ptr, len); GDEBUG(" %s", line); len -= consumed; ptr += consumed; } } static void app_decode_card_status( GBinderRemoteRequest* req) { GBinderReader reader; GBinderBuffer* buf; gbinder_remote_request_init_reader(req, &reader); /* RadioResponseInfo */ buf = gbinder_reader_read_buffer(&reader); if (buf) { const RadioResponseInfo* info = buf->data; GASSERT(sizeof(*info) == buf->size); GDEBUG("RadioResponseInfo: type=%d serial=%d error=%d", (int)info->type, (int)info->serial, (int)info->error); app_dump(buf->data, buf->size); gbinder_buffer_free(buf); /* CardStatus */ buf = gbinder_reader_read_buffer(&reader); if (buf) { guint i; const RadioCardStatus* status = buf->data; GINFO("CardStatus: state=%d pinState=%d gsmSubIndex=%d " "cdmsSubIndex=%d imsSubIndex=%d appCount=%u", (int)status->cardState, (int)status->universalPinState, (int)status->gsmUmtsSubscriptionAppIndex, (int)status->cdmaSubscriptionAppIndex, (int)status->imsSubscriptionAppIndex, status->numApps); GASSERT(sizeof(*status) == buf->size); app_dump(buf->data, buf->size); gbinder_buffer_free(buf); for (i = 0; i < status->numApps; i++) { const RadioAppStatus* app = status->apps.array + i; buf = gbinder_reader_read_buffer(&reader); GASSERT(buf->size == sizeof(RadioAppStatus)); GINFO("AppStatus: type=%u state=%u substate=%u aid=%s " "label=%s pin_replaced=%u pin1=%u pin2=%u", app->appType, app->appState, app->persoSubstate, app->aid.data.str, app->label.data.str, app->pinReplaced, app->pin1, app->pin2); app_dump(buf->data, buf->size); gbinder_buffer_free(buf); } } } } static GBinderLocalReply* IRadioIndication_transact( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { const char* iface = gbinder_remote_request_interface(req); GDEBUG("%s %u", iface, code); if (!g_strcmp0(iface, IFACE_INDICATION)) { /* Those should all be one-way */ GASSERT(flags & GBINDER_TX_FLAG_ONEWAY); *status = GBINDER_STATUS_OK; } else { *status = GBINDER_STATUS_FAILED; } return NULL; } static GBinderLocalReply* IRadioResponse_transact( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { App* app = user_data; const char* iface = gbinder_remote_request_interface(req); if (!g_strcmp0(iface, IFACE_RESPONSE)) { /* Those should all be one-way */ GASSERT(flags & GBINDER_TX_FLAG_ONEWAY); if (code == RESP_GET_ICC_CARD_STATUS_RESPONSE) { GDEBUG("%s getIccCardStatusResponse", iface); *status = GBINDER_STATUS_OK; app_decode_card_status(req); g_main_loop_quit(app->loop); return NULL; } } GDEBUG("%s %u", iface, code); *status = GBINDER_STATUS_FAILED; return NULL; } static void app_run( App* app) { const AppOptions* opt = app->opt; char* fqname = opt->fqname ? g_strdup(opt->fqname) : strchr(opt->name, '/') ? g_strdup(opt->name) : g_strconcat(IFACE_RADIO "/", opt->name, NULL); int status = 0; GBinderRemoteObject* remote = gbinder_remote_object_ref (gbinder_servicemanager_get_service_sync(app->sm, fqname, &status)); if (remote) { guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, app); guint sigint = g_unix_signal_add(SIGINT, app_signal, app); gulong death_id = gbinder_remote_object_add_death_handler (remote, app_remote_died, app); GBinderClient* client = gbinder_client_new(remote, IFACE_RADIO); GBinderLocalRequest* req; GBinderRemoteReply* reply; int status; GINFO("Connected to %s", fqname); app->ret = RET_OK; app->loop = g_main_loop_new(NULL, TRUE); /* IRadio::setResponseFunctions */ req = gbinder_client_new_request(client); gbinder_local_request_append_local_object(req, app->response); gbinder_local_request_append_local_object(req, app->indication); reply = gbinder_client_transact_sync_reply(client, REQ_RADIO_SET_RESPONSE_FUNCTIONS, req, &status); GDEBUG("setResponseFunctions status %d", status); gbinder_local_request_unref(req); gbinder_remote_reply_unref(reply); /* IRadio::getIccCardStatus */ req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, 1 /* serial */); status = gbinder_client_transact_sync_oneway(client, REQ_RADIO_GET_ICC_CARD_STATUS, req); GDEBUG("getIccCardStatus status %d", status); gbinder_local_request_unref(req); g_main_loop_run(app->loop); g_source_remove(sigtrm); g_source_remove(sigint); g_main_loop_unref(app->loop); gbinder_remote_object_remove_handler(remote, death_id); gbinder_remote_object_unref(remote); gbinder_client_unref(client); app->loop = NULL; } else { GERR("No such service: %s (%d)", fqname, status); } g_free(fqname); } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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" }, { "fqname", 'n', 0, G_OPTION_ARG_STRING, &opt->fqname, "Fully qualified name", "FQNAME" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[NAME]"); memset(opt, 0, sizeof(*opt)); gutil_log_timestamp = FALSE; gutil_log_set_type(GLOG_TYPE_STDERR, pname); gutil_log_default.level = GLOG_LEVEL_DEFAULT; g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { char* help; if (!opt->dev || !opt->dev[0]) opt->dev = g_strdup(DEFAULT_DEVICE); switch (argc) { case 2: opt->name = argv[1]; ok = TRUE; break; case 1: opt->name = DEFAULT_NAME; ok = TRUE; break; default: help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); break; } } 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.indication = gbinder_servicemanager_new_local_object(app.sm, IFACE_INDICATION, IRadioIndication_transact, &app); app.response = gbinder_servicemanager_new_local_object(app.sm, IFACE_RESPONSE, IRadioResponse_transact, &app); app_run(&app); gbinder_local_object_unref(app.indication); gbinder_local_object_unref(app.response); gbinder_servicemanager_unref(app.sm); } } g_free(opt.fqname); g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/tools/000077500000000000000000000000001526127616700150605ustar00rootroot00000000000000libgbinder-1.1.54/tools/Makefile000066400000000000000000000002321526127616700165150ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- all: %: @$(MAKE) -C binder-bridge $* @$(MAKE) -C binder-call $* @$(MAKE) -C binder-list $* @$(MAKE) -C binder-ping $* libgbinder-1.1.54/tools/README000066400000000000000000000000331526127616700157340ustar00rootroot00000000000000Binder command line tools. libgbinder-1.1.54/tools/binder-bridge/000077500000000000000000000000001526127616700175555ustar00rootroot00000000000000libgbinder-1.1.54/tools/binder-bridge/Makefile000066400000000000000000000001301526127616700212070ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-bridge include ../../test/common/Makefile libgbinder-1.1.54/tools/binder-bridge/binder-bridge.c000066400000000000000000000133711526127616700224230ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2021 Jolla Ltd. * Copyright (C) 2021 Slava Monich * * 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 #include #include #define RET_OK (0) #define RET_NODEV (1) #define RET_INVARG (2) typedef struct app_options { const char* src; const char* dest; char* src_name; const char* dest_name; const char** ifaces; } AppOptions; static gboolean app_signal( gpointer loop) { GINFO("Caught signal, shutting down..."); g_main_loop_quit(loop); return G_SOURCE_CONTINUE; } static int app_run( const AppOptions* opt) { int ret = RET_NODEV; GBinderServiceManager* src = gbinder_servicemanager_new(opt->src); if (src) { GBinderServiceManager* dest = gbinder_servicemanager_new(opt->dest); if (dest) { GMainLoop* loop = g_main_loop_new(NULL, TRUE); guint sigtrm = g_unix_signal_add(SIGTERM, app_signal, loop); guint sigint = g_unix_signal_add(SIGINT, app_signal, loop); GBinderBridge* bridge = opt->ifaces ? gbinder_bridge_new2 (opt->src_name, opt->dest_name, opt->ifaces, src, dest) : gbinder_bridge_new3(opt->src_name, opt->dest_name, src, dest); g_main_loop_run(loop); if (sigtrm) g_source_remove(sigtrm); if (sigint) g_source_remove(sigint); g_main_loop_unref(loop); gbinder_bridge_free(bridge); gbinder_servicemanager_unref(dest); ret = RET_OK; } else { GERR("No servicemanager at %s", opt->dest); } gbinder_servicemanager_unref(src); } else { GERR("No servicemanager at %s", opt->src); } return ret; } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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_NONE; return TRUE; } static gboolean app_init( AppOptions* opt, int argc, char* argv[]) { gboolean ok = FALSE; GOptionEntry entries[] = { { "source", 's', 0, G_OPTION_ARG_STRING, &opt->src_name, "Register a different name on source", "NAME" }, { "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 }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("SRC DST NAME [IFACES...]"); g_option_context_add_main_entries(options, entries, NULL); g_option_context_set_summary(options, "Forwards calls from device SRC to device DST. IFACES are optional " "for direct-name services (e.g. AIDL)."); if (g_option_context_parse(options, &argc, &argv, &error)) { if (argc >= 4) { int i; const int first_iface = 4; opt->src = argv[1]; opt->dest = argv[2]; opt->dest_name = argv[3]; if (argc > first_iface) { opt->ifaces = g_new(const char*, argc - first_iface + 1); for (i = first_iface; i < argc; i++) { opt->ifaces[i - first_iface] = argv[i]; } opt->ifaces[i - first_iface] = NULL; } ok = TRUE; } else { char* 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[]) { AppOptions opt; int ret = RET_INVARG; memset(&opt, 0, sizeof(opt)); if (app_init(&opt, argc, argv)) { ret = app_run(&opt); } g_free(opt.src_name); g_free(opt.ifaces); return ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/tools/binder-call/000077500000000000000000000000001526127616700172345ustar00rootroot00000000000000libgbinder-1.1.54/tools/binder-call/Makefile000066400000000000000000000077721526127616700207110ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- .PHONY: all debug release install 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 LIB_DIR = ../.. DEBUG_BUILD_DIR = $(BUILD_DIR)/debug RELEASE_BUILD_DIR = $(BUILD_DIR)/release # # Tools and flags # PKG_CONFIG ?= pkg-config CC ?= $(CROSS_COMPILE)gcc LD = $(CC) WARNINGS = -Wall DEFINES += -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32 \ -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_MAX_ALLOWED INCLUDES = -I$(LIB_DIR)/include -I$(SRC_DIR) CFLAGS += -fPIC $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \ $(shell $(PKG_CONFIG) --cflags $(PKGS)) LDFLAGS += -pie $(shell $(PKG_CONFIG) --libs $(PKGS)) QUIET_MAKE = $(MAKE) --no-print-directory DEBUG_FLAGS = -g RELEASE_FLAGS = KEEP_SYMBOLS ?= 0 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) -I$(DEBUG_BUILD_DIR) -DDEBUG RELEASE_CFLAGS = $(CFLAGS) $(RELEASE_FLAGS) -I $(RELEASE_BUILD_DIR) -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_GEN_FILES = $(GEN_SRC:%=$(DEBUG_BUILD_DIR)/%) RELEASE_GEN_FILES = $(GEN_SRC:%=$(RELEASE_BUILD_DIR)/%) DEBUG_SO = $(LIB_DIR)/$(DEBUG_SO_FILE) RELEASE_SO = $(LIB_DIR)/$(RELEASE_SO_FILE) # # Dependencies # DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) ifneq ($(MAKECMDGOALS),clean) ifneq ($(strip $(DEPS)),) -include $(DEPS) endif endif $(DEBUG_GEN_FILES): | $(DEBUG_BUILD_DIR) $(RELEASE_GEN_FILES): | $(RELEASE_BUILD_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) $(RELEASE_BUILD_DIR): mkdir -p $@ $(DEBUG_BUILD_DIR)/%.tab.c: $(SRC_DIR)/%.y bison -pcmdline -bcmdline -d -o $@ $< $(RELEASE_BUILD_DIR)/%.tab.c: $(SRC_DIR)/%.y bison -pcmdline -bcmdline -d -o $@ $< $(DEBUG_BUILD_DIR)/lex.%.c: $(SRC_DIR)/%.l $(DEBUG_BUILD_DIR)/%.tab.c flex -o $@ $< $(RELEASE_BUILD_DIR)/lex.%.c: $(SRC_DIR)/%.l $(RELEASE_BUILD_DIR)/%.tab.c flex -o $@ $< $(DEBUG_BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(DEBUG_BUILD_DIR)/%.o: $(DEBUG_BUILD_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 $@ $(RELEASE_BUILD_DIR)/%.o: $(RELEASE_BUILD_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 $@ libgbinder-1.1.54/tools/binder-call/binder-call.c000066400000000000000000000713671526127616700215720ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021 Franz-Josef Haider * * 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 #include #include #include #include #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"; struct transaction_and_reply* ast; 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 %u", (guint)(*((guint8*)v->value))); } if (parent_idx == -1) { gbinder_writer_append_int8(&app->writer, *((guint8*)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 %d", *((int*)v->value)); } 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 %" G_GINT64_MODIFIER "d", *((gint64*)v->value)); } 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 %g", (double)*((float*)v->value)); } 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 %g", *((double*)v->value)); } 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 %s", (char*)v->value); } 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; } else 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, 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) { 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; void* 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("%" G_GINT32_FORMAT " ", 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("%" G_GINT64_FORMAT " ", 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: if (cur_pass == PRINT_REPLY) GDEBUG("string8"); printf("\"%s\" ", gbinder_reader_read_string8(&app->reader)); /* 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); offset += go_through_reply_ast(app, t->data, NULL, gbinder_reader_read_hidl_struct1(&app->reader, sl), PRINT_REPLY); } else { offset += go_through_reply_ast(app, t->data, NULL, ((char*)buf) + offset, 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; 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); 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, "Performs binder transactions from the command line.\n\n" "NAME is the name of the object to call, registered with servicemanager.\n" "For example \"android.hardware.sensors@1.0::ISensors/default\".\n\n" "CODE is the transaction id (must be >=1).\n\n" "Optional transaction arguments follow the transaction code.\n" "Possible arguments are:\n\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\"[.*]\"u 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\n" "The structure of the reply follows the \"reply\" keyword.\n" "The following types are accepted:\n\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 where TYPE can be any of the possible types described 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 described here.\n\n" "The following example calls getSensorsList method on \"android.hardware.sensors@1.0::ISensors/default\"\n" "service:\n\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_set_type(GLOG_TYPE_STDERR, pname); if (g_option_context_parse(options, &argc, &argv, &error)) { int i; /* * Remove the "--" argument. If any of our arguments is a negative * number, the user will have to add the "--" flag to stop the parser. * But "--" is still passed to us and we have to ignore it. */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--")) { if (i < (argc - 1)) { memmove(argv + i, argv + (i + 1), sizeof(char*) * (argc - i - 1)); } i--; argc--; /* * There's no need to have more than one "--", but let's * remove any number of those. */ } } if (argc > 2) { opt->argc = argc; opt->argv = argv; ok = TRUE; } else { char* 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.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/tools/binder-call/binder-call.h000066400000000000000000000051231526127616700215620ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2021-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BINDER_CALL_H__ #define BINDER_CALL_H__ #include typedef struct app_options { char* dev; 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; 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); int cmdlinelex( void* args); extern struct transaction_and_reply* ast; #endif libgbinder-1.1.54/tools/binder-call/cmdline.l000066400000000000000000000060011526127616700210210ustar00rootroot00000000000000D [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 #include "cmdline.tab.h" #include "binder-call.h" #define YY_SKIP_YYWRAP 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; } %} %option never-interactive noinput nounput %% "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); } libgbinder-1.1.54/tools/binder-call/cmdline.y000066400000000000000000000216731526127616700210520ustar00rootroot00000000000000%{ #include #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 values %type struct_values %type vec_values %type value_specifiers %type translation_unit %type values_list %type specifiers_list %type specifiers %type type_specifier %type vec_specifier %type struct_specifier %type struct_declaration_list %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 #include #include #include 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]); } libgbinder-1.1.54/tools/binder-list/000077500000000000000000000000001526127616700172745ustar00rootroot00000000000000libgbinder-1.1.54/tools/binder-list/Makefile000066400000000000000000000001261526127616700207330ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-list include ../../test/common/Makefile libgbinder-1.1.54/tools/binder-list/binder-list.c000066400000000000000000000147661526127616700216720ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2020 Jolla Ltd. * Copyright (C) 2018-2020 Slava Monich * * 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 #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEV_DEFAULT GBINDER_DEFAULT_HWBINDER typedef struct app_options { char* dev; const char* service; gboolean async; } AppOptions; typedef struct app { const AppOptions* opt; GMainLoop* loop; GBinderServiceManager* sm; int ret; } App; static const char pname[] = "binder-list"; static void app_print_strings( char** strv) { if (strv) { while (*strv) { printf("%s\n", *strv); strv++; } } } static gboolean app_list_done( GBinderServiceManager* sm, char** services, void* user_data) { App* app = user_data; if (services) { app_print_strings(services); app->ret = RET_OK; } g_main_loop_quit(app->loop); return FALSE; } static void app_get_service_done( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { App* app = user_data; if (obj) { printf("%s\n", app->opt->service); app->ret = RET_OK; } else { GERR("No such service: %s (%d)", app->opt->service, status); } g_main_loop_quit(app->loop); } static void app_async( App* app) { const char* name = app->opt->service; app->loop = g_main_loop_new(NULL, TRUE); if (name) { gbinder_servicemanager_get_service(app->sm, name, app_get_service_done, app); } else { gbinder_servicemanager_list(app->sm, app_list_done, app); } g_main_loop_run(app->loop); g_main_loop_unref(app->loop); app->loop = NULL; } static void app_sync( App* app) { const AppOptions* opt = app->opt; if (opt->service) { int status = 0; GBinderRemoteObject* obj = gbinder_servicemanager_get_service_sync (app->sm, opt->service, &status); if (obj) { printf("%s\n", opt->service); app->ret = RET_OK; } else { GERR("No such service: %s (%d)", opt->service, status); } } else { char** services = gbinder_servicemanager_list_sync(app->sm); if (services) { app_print_strings(services); g_strfreev(services); app->ret = RET_OK; } } } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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 }, { "async", 'a', 0, G_OPTION_ARG_NONE, &opt->async, "Perform operations asynchronously", NULL }, { "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev, "Binder device [" DEV_DEFAULT "]", "DEVICE" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[SERVICE]"); memset(opt, 0, sizeof(*opt)); gutil_log_set_type(GLOG_TYPE_STDERR, pname); g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { char* help; if (!opt->dev || !opt->dev[0]) opt->dev = g_strdup(DEV_DEFAULT); switch (argc) { case 2: opt->service = argv[1]; /* no break */ case 1: ok = TRUE; break; default: help = g_option_context_get_help(options, TRUE, NULL); fprintf(stderr, "%s", help); g_free(help); break; } } 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 (gbinder_servicemanager_wait(app.sm, -1)) { if (opt.async) { app_async(&app); } else { app_sync(&app); } gbinder_servicemanager_unref(app.sm); } } g_free(opt.dev); return app.ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/tools/binder-ping/000077500000000000000000000000001526127616700172565ustar00rootroot00000000000000libgbinder-1.1.54/tools/binder-ping/Makefile000066400000000000000000000001261526127616700207150ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = binder-ping include ../../test/common/Makefile libgbinder-1.1.54/tools/binder-ping/binder-ping.c000066400000000000000000000126651526127616700216320ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * * 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 #include #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) #define RET_ERR (3) #define DEFAULT_BINDER GBINDER_DEFAULT_HWBINDER #define AIDL_PING_TRANSACTION GBINDER_FOURCC('_','P','N','G') #define HIDL_PING_TRANSACTION GBINDER_FOURCC(0x0f,'P','N','G') typedef struct app_options { const char* fqname; char* dev; guint32 ping_code; const char* iface; } AppOptions; static int app_run( const AppOptions* opt) { int ret = RET_NOTFOUND; GBinderServiceManager* sm = gbinder_servicemanager_new(opt->dev); if (sm) { int status = 0; GBinderRemoteObject* remote = gbinder_servicemanager_get_service_sync (sm, opt->fqname, &status); if (remote) { int status; GBinderClient* client = gbinder_client_new(remote, opt->iface); GBinderRemoteReply* reply = gbinder_client_transact_sync_reply (client, opt->ping_code, NULL, &status); if (reply) { GINFO("OK"); ret = RET_OK; } else { GERR("Ping failed (%d)", status); ret = RET_ERR; } gbinder_remote_reply_unref(reply); gbinder_client_unref(client); } else { GERR("%s not found", opt->fqname); } gbinder_servicemanager_unref(sm); } else { GERR("No servicemanager at %s", opt->dev); } return ret; } static gboolean app_log_verbose( const gchar* name, const gchar* value, gpointer data, GError** error) { gutil_log_default.level = 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_NONE; 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_BINDER "]", "DEVICE" }, { NULL } }; GError* error = NULL; GOptionContext* options = g_option_context_new("[FQNAME]"); g_option_context_add_main_entries(options, entries, NULL); if (g_option_context_parse(options, &argc, &argv, &error)) { if (!opt->dev || !opt->dev[0]) { opt->dev = g_strdup(DEFAULT_BINDER); } if (argc == 2) { opt->fqname = argv[1]; if (g_strcmp0(opt->dev, GBINDER_DEFAULT_BINDER)) { opt->ping_code = HIDL_PING_TRANSACTION; opt->iface = "android.hidl.base@1.0::IBase"; } else { opt->ping_code = AIDL_PING_TRANSACTION; opt->iface = "android.os.IBinder"; } ok = TRUE; } else { char* 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[]) { AppOptions opt; int ret = RET_INVARG; memset(&opt, 0, sizeof(opt)); if (app_init(&opt, argc, argv)) { ret = app_run(&opt); } g_free(opt.dev); return ret; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/000077500000000000000000000000001526127616700146775ustar00rootroot00000000000000libgbinder-1.1.54/unit/Makefile000066400000000000000000000020061526127616700163350ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- all: %: @$(MAKE) -C unit_bridge $* @$(MAKE) -C unit_buffer $* @$(MAKE) -C unit_client $* @$(MAKE) -C unit_config $* @$(MAKE) -C unit_driver $* @$(MAKE) -C unit_eventloop $* @$(MAKE) -C unit_fmq $* @$(MAKE) -C unit_ipc $* @$(MAKE) -C unit_local_object $* @$(MAKE) -C unit_local_reply $* @$(MAKE) -C unit_local_request $* @$(MAKE) -C unit_log $* @$(MAKE) -C unit_protocol $* @$(MAKE) -C unit_proxy_object $* @$(MAKE) -C unit_reader $* @$(MAKE) -C unit_remote_object $* @$(MAKE) -C unit_remote_reply $* @$(MAKE) -C unit_remote_request $* @$(MAKE) -C unit_servicemanager $* @$(MAKE) -C unit_servicemanager_aidl $* @$(MAKE) -C unit_servicemanager_aidl2 $* @$(MAKE) -C unit_servicemanager_aidl3 $* @$(MAKE) -C unit_servicemanager_aidl5 $* @$(MAKE) -C unit_servicemanager_aidl6 $* @$(MAKE) -C unit_servicemanager_hidl $* @$(MAKE) -C unit_servicename $* @$(MAKE) -C unit_servicepoll $* @$(MAKE) -C unit_writer $* clean: unitclean rm -f coverage/*.gcov rm -fr coverage/report libgbinder-1.1.54/unit/common/000077500000000000000000000000001526127616700161675ustar00rootroot00000000000000libgbinder-1.1.54/unit/common/Makefile000066400000000000000000000122731526127616700176340ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- .PHONY: unitclean clean cleaner all debug release coverage .PHONY: debug_lib release_lib coverage_lib .PHONY: valgrind test test-banner test-debug test-release # # Real test makefile defines EXE (and possibly SRC) and includes this one. # ifndef EXE ${error EXE not defined} endif SRC ?= $(EXE).c COMMON_SRC ?= test_binder.c test_main.c # # Required packages # PKGS += glib-2.0 gobject-2.0 ifeq ($(LIBGLIBUTIL_PATH),) # Assume that libglibutil devel package is installed PKGS += libglibutil else # Side-by-side build INCLUDES += -I$(LIBGLIBUTIL_PATH)/include DEBUG_LIBS = -L$(LIBGLIBUTIL_PATH)/build/debug -lglibutil RELEASE_LIBS = -L$(LIBGLIBUTIL_PATH)/build/release -lglibutil DEBUG_DEPS = libglibutil_debug RELEASE_DEPS = libglibutil_release .PHONY: libglibutil_debug libglibutil_release libglibutil_debug: $(MAKE) -C $(LIBGLIBUTIL_PATH) debug libglibutil_release: $(MAKE) -C $(LIBGLIBUTIL_PATH) release endif # # Default target # all: debug release # # Directories # SRC_DIR = . LIB_DIR = ../.. COMMON_DIR = ../common BUILD_DIR = build DEBUG_BUILD_DIR = $(BUILD_DIR)/debug RELEASE_BUILD_DIR = $(BUILD_DIR)/release COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage # # Tools and flags # PKG_CONFIG ?= pkg-config CC ?= $(CROSS_COMPILE)gcc LD = $(CC) WARNINGS += -Wall -Wno-deprecated-declarations DEFINES += -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32 \ -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_MAX_ALLOWED INCLUDES += -I$(COMMON_DIR) -I$(LIB_DIR)/src -I$(LIB_DIR)/include BASE_FLAGS = -fPIC -g BASE_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS) BASE_CFLAGS = $(BASE_FLAGS) $(CFLAGS) FULL_CFLAGS = $(BASE_CFLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \ $(shell $(PKG_CONFIG) --cflags $(PKGS)) FULL_LDFLAGS = $(BASE_LDFLAGS) LIBS = $(shell $(PKG_CONFIG) --libs $(PKGS)) -lpthread QUIET_MAKE = $(MAKE) --no-print-directory DEBUG_LDFLAGS = $(FULL_LDFLAGS) RELEASE_LDFLAGS = $(FULL_LDFLAGS) COVERAGE_LDFLAGS = $(FULL_LDFLAGS) --coverage -fprofile-update=atomic DEBUG_CFLAGS = $(FULL_CFLAGS) -DDEBUG RELEASE_CFLAGS = $(FULL_CFLAGS) -O2 COVERAGE_CFLAGS = $(FULL_CFLAGS) -O0 --coverage -fprofile-update=atomic DEBUG_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_lib) RELEASE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_lib) COVERAGE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_coverage_lib) DEBUG_LIB = $(LIB_DIR)/$(DEBUG_LIB_FILE) RELEASE_LIB = $(LIB_DIR)/$(RELEASE_LIB_FILE) COVERAGE_LIB = $(LIB_DIR)/$(COVERAGE_LIB_FILE) DEBUG_LIBS += $(DEBUG_LIB) $(LIBS) RELEASE_LIBS += $(RELEASE_LIB) $(LIBS) COVERAGE_LIBS += $(COVERAGE_LIB) $(LIBS) # # Files # DEBUG_OBJS = \ $(COMMON_SRC:%.c=$(DEBUG_BUILD_DIR)/common_%.o) \ $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) RELEASE_OBJS = \ $(COMMON_SRC:%.c=$(RELEASE_BUILD_DIR)/common_%.o) \ $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) COVERAGE_OBJS = \ $(COMMON_SRC:%.c=$(COVERAGE_BUILD_DIR)/common_%.o) \ $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o) # # Dependencies # DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) ifneq ($(MAKECMDGOALS),clean) ifneq ($(strip $(DEPS)),) -include $(DEPS) endif endif $(DEBUG_LIB): | debug_lib $(RELEASE_LIB): | release_lib $(COVERAGE_LIB): | coverage_lib $(DEBUG_OBJS): | $(DEBUG_BUILD_DIR) $(DEBUG_DEPS) $(RELEASE_OBJS): | $(RELEASE_BUILD_DIR) $(RELEASE_DEPS) $(COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR) # # Rules # DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE) RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE) COVERAGE_EXE = $(COVERAGE_BUILD_DIR)/$(EXE) debug: debug_lib $(DEBUG_EXE) release: release_lib $(RELEASE_EXE) coverage: coverage_lib $(COVERAGE_EXE) unitclean: rm -f *~ rm -fr $(BUILD_DIR) clean: unitclean cleaner: unitclean @$(MAKE) -C $(LIB_DIR) clean test-banner: @echo "===========" $(EXE) "=========== " test-debug: test-banner debug @$(DEBUG_EXE) $(TEST_OPTS) test-release: test-banner release @$(RELEASE_EXE) $(TEST_OPTS) test: test-release valgrind: test-banner release @G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --tool=memcheck --leak-check=full --show-possibly-lost=no $(RELEASE_EXE) $(TEST_OPTS) $(DEBUG_BUILD_DIR): mkdir -p $@ $(RELEASE_BUILD_DIR): mkdir -p $@ $(COVERAGE_BUILD_DIR): mkdir -p $@ $(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 $@ $(COVERAGE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c $(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(DEBUG_BUILD_DIR)/common_%.o : $(COMMON_DIR)/%.c $(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(RELEASE_BUILD_DIR)/common_%.o : $(COMMON_DIR)/%.c $(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(COVERAGE_BUILD_DIR)/common_%.o : $(COMMON_DIR)/%.c $(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@ $(DEBUG_EXE): $(DEBUG_LIB) $(DEBUG_OBJS) $(LD) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) $(DEBUG_LIBS) -o $@ $(RELEASE_EXE): $(RELEASE_LIB) $(RELEASE_OBJS) $(LD) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) $(RELEASE_LIBS) -o $@ $(COVERAGE_EXE): $(COVERAG_LIB) $(COVERAGE_OBJS) $(LD) $(COVERAGE_LDFLAGS) $(COVERAGE_OBJS) $(COVERAGE_LIBS) -o $@ debug_lib: $(MAKE) -C $(LIB_DIR) $@ release_lib: $(MAKE) -C $(LIB_DIR) $@ coverage_lib: $(MAKE) -C $(LIB_DIR) $@ libgbinder-1.1.54/unit/common/test_binder.c000066400000000000000000001326771526127616700206550ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * Copyright (C) 2023 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_ipc.h" #include "gbinder_local_object_p.h" #include "gbinder_system.h" #define GLOG_MODULE_NAME test_binder_log #include GLOG_MODULE_DEFINE2("test_binder", gutil_log_default); #include #include #include #include #include #include #include #include #include #include #include #define gettid() ((int)syscall(SYS_gettid)) #define BINDER_PACKED __attribute__((packed)) #define BINDER_CMDSIZE(code) (_IOC_SIZE(code) + 4) static GHashTable* test_fd_map = NULL; /* fd => TestBinderFd */ static GHashTable* test_path_map = NULL; /* path => TestBinderNode */ static GPrivate test_tx_stack = G_PRIVATE_INIT(NULL); static gint32 last_auto_handle = 0; G_LOCK_DEFINE_STATIC(test_binder); static GMainLoop* test_binder_exit_loop = NULL; #define public_fd fd[0] #define private_fd fd[1] #define BINDER_VERSION _IOWR('b', 9, gint32) #define BINDER_SET_MAX_THREADS _IOW('b', 5, guint32) #define BINDER_BUFFER_FLAG_HAS_PARENT 0x01 #define TF_ONE_WAY 0x01 #define TF_ROOT_OBJECT 0x04 #define TF_STATUS_CODE 0x08 #define TF_ACCEPT_FDS 0x10 typedef struct test_binder_io TestBinderIo; typedef struct test_binder_node TestBinderNode; typedef struct test_binder_cmd TestBinderCmd; /* * tid > 0 for this specific thread * tid = 0 for any thread (ANY_THREAD) * tid < 0 special value (TX_THREAD etc) */ struct test_binder_cmd { int tid; const guint32* data; TestBinderCmd* next; }; struct test_binder_io { int version; int write_read_request; int (*handle_write_read)(TestBinderNode* node, void* data); }; struct test_binder_node { gint refcount; int owner; int fd[2]; char* path; gint ignore_dead_object; const char* name; const TestBinderIo* io; GMutex mutex; GUtilIntArray* loopers; /* tids of the loopers */ GHashTable* object_map; /* GBinderLocalObject* => handle */ GHashTable* handle_map; /* handle => GBinderLocalObject* */ GHashTable* destroy_map; /* gpointer => GDestroyNotify */ TestBinderCmd* cmd_first; TestBinderCmd* cmd_last; }; typedef struct test_binder_fd { int fd; TestBinderNode* node; } TestBinderFd; typedef void (*TestBinderPushDataFunc)( int fd, const void* data); typedef struct binder_write_read_64 { guint64 write_size; guint64 write_consumed; guint64 write_buffer; guint64 read_size; guint64 read_consumed; guint64 read_buffer; } BinderWriteRead64; typedef union binder_target_64 { guint32 handle; guint64 ptr; } BinderTarget64; typedef struct binder_transaction_data_64 { BinderTarget64 target; guint64 cookie; guint32 code; guint32 flags; gint32 sender_pid; gint32 sender_euid; guint64 data_size; guint64 offsets_size; guint64 data_buffer; guint64 data_offsets; } BinderTransactionData64; typedef struct binder_transaction_data_sg_64 { BinderTransactionData64 tx; guint64 buffers_size; } BinderTransactionDataSg64; typedef struct binder_pre_cookie_64 { guint64 ptr; guint64 cookie; } BinderPtrCookie64; typedef struct binder_handle_cookie_64 { guint32 handle; guint64 cookie; } BINDER_PACKED BinderHandleCookie64; typedef struct binder_object_64 { guint32 type; /* BINDER_TYPE_BINDER */ guint32 flags; union { guint32 handle; guint64 ptr; }; guint64 cookie; } BinderObject64; typedef struct binder_buffer_64 { guint32 type; /* BINDER_TYPE_PTR */ guint32 flags; guint64 buffer; guint64 length; guint64 parent; guint64 parent_offset; } BinderBuffer64; #define BC_TRANSACTION_64 _IOW('c', 0, BinderTransactionData64) #define BC_REPLY_64 _IOW('c', 1, BinderTransactionData64) #define BC_FREE_BUFFER_64 _IOW('c', 3, guint64) #define BC_INCREFS _IOW('c', 4, guint32) #define BC_ACQUIRE _IOW('c', 5, guint32) #define BC_RELEASE _IOW('c', 6, guint32) #define BC_DECREFS _IOW('c', 7, guint32) #define BC_ACQUIRE_DONE_64 _IOW('c', 9, BinderPtrCookie64) #define BC_ENTER_LOOPER _IO('c', 12) #define BC_EXIT_LOOPER _IO('c', 13) #define BC_REQUEST_DEATH_NOTIFICATION_64 _IOW('c', 14, BinderHandleCookie64) #define BC_CLEAR_DEATH_NOTIFICATION_64 _IOW('c', 15, BinderHandleCookie64) #define BC_DEAD_BINDER_DONE _IOW('c', 16, guint64) #define BC_TRANSACTION_SG_64 _IOW('c', 17, BinderTransactionDataSg64) #define BC_REPLY_SG_64 _IOW('c', 18, BinderTransactionDataSg64) #define BR_TRANSACTION_64 _IOR('r', 2, BinderTransactionData64) #define BR_REPLY_64 _IOR('r', 3, BinderTransactionData64) #define BR_DEAD_REPLY _IO('r', 5) #define BR_TRANSACTION_COMPLETE _IO('r', 6) #define BR_INCREFS_64 _IOR('r', 7, BinderPtrCookie64) #define BR_ACQUIRE_64 _IOR('r', 8, BinderPtrCookie64) #define BR_RELEASE_64 _IOR('r', 9, BinderPtrCookie64) #define BR_DECREFS_64 _IOR('r', 10, BinderPtrCookie64) #define BR_NOOP _IO('r', 12) #define BR_DEAD_BINDER_64 _IOR('r', 15, guint64) #define BR_CLEAR_DEATH_NOTIFICATION_DONE_64 _IOR('r', 16, guint64) #define BR_FAILED_REPLY _IO('r', 17) typedef enum cmd_dequeue_flags { CMD_DEQUEUE_FLAGS_NONE = 0, CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION = 0x01, CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION_COMPLETE = 0x02, CMD_DEQUEUE_FLAG_STOP_AT_REPLY = 0x04, } CMD_DEQUEUE_FLAGS; #define test_binder_cmd_free(cmd) g_free(cmd) /* See comment in test_binder_node_free() explaining what this is for */ typedef struct test_binder_destroy_data { GDestroyNotify destroy; void* data; } TestBinderDestroyData; static void test_binder_destroy_data_free1( gpointer list_data) { TestBinderDestroyData* entry = list_data; entry->destroy(entry->data); g_free(entry); } static void test_binder_destroy_data_free( gpointer list) { g_slist_free_full(list, test_binder_destroy_data_free1); } static GPrivate test_destroy_data = G_PRIVATE_INIT (test_binder_destroy_data_free); static void test_binder_destroy_data_add( GDestroyNotify destroy, void* data) { GSList* list = g_private_get(&test_destroy_data); TestBinderDestroyData* entry = g_new(TestBinderDestroyData, 1); entry->destroy = destroy ? destroy : g_free; entry->data = data; g_private_set(&test_destroy_data, g_slist_append(list, entry)); } static void test_binder_destroy_data_flush() { GSList* list = g_private_get(&test_destroy_data); g_private_set(&test_destroy_data, NULL); test_binder_destroy_data_free(list); } static TestBinderNode* test_binder_node_new( const char* path, const TestBinderIo* io) { TestBinderNode* node = g_new0(TestBinderNode, 1); const char* name; g_atomic_int_set(&node->refcount, 1); g_assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, node->fd)); node->path = g_strdup(path); name = strrchr(node->path, '/'); node->name = (name ? (name + 1) : node->path); node->io = io; g_mutex_init(&node->mutex); node->loopers = gutil_int_array_new(); node->object_map = g_hash_table_new(g_direct_hash, g_direct_equal); node->handle_map = g_hash_table_new(g_direct_hash, g_direct_equal); node->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal); GDEBUG("Created node %s (%d)", path, node->public_fd); return node; } static void test_binder_node_free( TestBinderNode* node) { TestBinderCmd* cmd; GHashTableIter it; gpointer key, value; /* * There's a tiny race condition on exit. The last handle may be * closed before all looper threads have finished. Those loopers * may still keep pointers to the buffers received from the binder. * * We have to postpone destruction of those buffers until * gbinder_ipc_exit() is done (or until the thread holding * the last reference to TestBinderNode has exited). */ g_hash_table_iter_init(&it, node->destroy_map); while (g_hash_table_iter_next(&it, &key, &value)) { test_binder_destroy_data_add(value, key); } g_assert_cmpuint(g_hash_table_size(node->object_map), == ,0); g_assert_cmpuint(g_hash_table_size(node->handle_map), == ,0); while ((cmd = node->cmd_first)) { node->cmd_first = cmd->next; test_binder_cmd_free(cmd); } GDEBUG("Destroyed node %s (%d)", node->path, node->public_fd); g_hash_table_unref(node->object_map); g_hash_table_unref(node->handle_map); g_hash_table_unref(node->destroy_map); gutil_int_array_free(node->loopers, TRUE); g_assert(!close(node->fd[0])); g_assert(!close(node->fd[1])); g_mutex_clear(&node->mutex); g_free(node->path); g_free(node); } static void test_binder_node_unref( TestBinderNode* node) { if (node && g_atomic_int_dec_and_test(&node->refcount)) { G_LOCK(test_binder); if (test_path_map) { if (g_hash_table_lookup(test_path_map, node->path) != node || g_atomic_int_get(&node->refcount) > 0) { /* Someone else has re-referenced or deleted this object */ node = NULL; } else { /* Remove it from the map and check if it's the last one */ g_assert(g_hash_table_remove(test_path_map, node->path)); if (!g_hash_table_size(test_path_map)) { g_hash_table_unref(test_path_map); test_path_map = NULL; if (test_binder_exit_loop) { GDEBUG("All nodes are gone"); test_quit_later(test_binder_exit_loop); } } } } G_UNLOCK(test_binder); if (node) { test_binder_node_free(node); } } } static TestBinderNode* test_binder_node_ref( TestBinderNode* node) { if (node) { g_atomic_int_inc(&node->refcount); } return node; } static void test_binder_node_lock( TestBinderNode* node) { g_mutex_lock(&node->mutex); g_assert_cmpint(node->owner, == ,0); node->owner = gettid(); } static void test_binder_node_unlock( TestBinderNode* node) { g_assert_cmpint(node->owner, == ,gettid()); node->owner = 0; g_mutex_unlock(&node->mutex); } static TestBinderFd* test_binder_fd_new( TestBinderNode* node) { TestBinderFd* binder_fd = g_new0(TestBinderFd, 1); binder_fd->fd = dup(node->public_fd); binder_fd->node = test_binder_node_ref(node); GDEBUG("Opened fd %d %s", binder_fd->fd, node->path); return binder_fd; } static void test_binder_fd_free( TestBinderFd* binder_fd) { test_binder_node_unref(binder_fd->node); close(binder_fd->fd); g_free(binder_fd); } static TestBinderNode* test_binder_node_ref_from_fd( int fd) { TestBinderNode* node = NULL; /* * On exit, gbinder_system_ioctl() may be called after the binder * device has been closed. */ if (fd >= 0) { G_LOCK(test_binder); if (test_fd_map) { TestBinderFd* binder_fd = g_hash_table_lookup (test_fd_map, GINT_TO_POINTER(fd)); if (binder_fd) { node = test_binder_node_ref(binder_fd->node); } } G_UNLOCK(test_binder); } return node; } static int test_binder_cmd_size( const TestBinderCmd* cmd) { return cmd ? BINDER_CMDSIZE(cmd->data[0]) : 0; } static guint32 test_binder_cmd_code( const TestBinderCmd* cmd) { return cmd ? cmd->data[0] : 0; } static void* test_binder_cmd_payload( const TestBinderCmd* cmd) { return cmd ? (void*)(cmd->data + 1) : NULL; } static TestBinderCmd* test_binder_cmd_new( int tid, const void* data) { const gsize size = _IOC_SIZE(((guint32*)data)[0]) + 4; TestBinderCmd* cmd = g_malloc(sizeof(TestBinderCmd) + size); void* storage = cmd + 1; memcpy(storage, data, size); cmd->tid = tid; cmd->data = storage; cmd->next = NULL; return cmd; } static void test_binder_node_queue_cmd_locked( TestBinderNode* node, TestBinderCmd* cmd) { guint8 ping = 0; g_assert(!cmd->next); if (node->cmd_last) { g_assert(node->cmd_first); node->cmd_last->next = cmd; node->cmd_last = cmd; } else { g_assert(!node->cmd_first); node->cmd_first = node->cmd_last = cmd; } g_assert_cmpint(write(node->private_fd, &ping, 1), > ,0); } static void test_binder_node_queue_cmd( TestBinderNode* node, TestBinderCmd* cmd) { /* Lock */ test_binder_node_lock(node); test_binder_node_queue_cmd_locked(node, cmd); test_binder_node_unlock(node); /* Unlock */ } #define test_binder_node_queue_cmd_data(node,tid,ptr) \ test_binder_node_queue_cmd(node, test_binder_cmd_new(tid, ptr)) #define test_binder_node_queue_cmd_data_locked(node,tid,ptr) \ test_binder_node_queue_cmd_locked(node, test_binder_cmd_new(tid, ptr)) static TestBinderCmd* test_binder_node_dequeue_cmd_locked( TestBinderNode* node, int max_size, CMD_DEQUEUE_FLAGS flags) { TestBinderCmd* prev = NULL; TestBinderCmd* cmd; const int tid = gettid(); for (cmd = node->cmd_first; cmd; cmd = cmd->next) { if (!cmd->tid || cmd->tid == tid || (cmd->tid == LOOPER_THREAD && gutil_int_array_contains(node->loopers, tid))) { const guint32 code = test_binder_cmd_code(cmd); BinderTransactionData64* tx = NULL; /* The command matches the tid criteria */ switch (code) { case BR_TRANSACTION_64: if (flags & CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION) { cmd = NULL; } else { tx = test_binder_cmd_payload(cmd); } break; case BR_TRANSACTION_COMPLETE: if (flags & CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION_COMPLETE) { cmd = NULL; } break; case BR_REPLY_64: if (flags & CMD_DEQUEUE_FLAG_STOP_AT_REPLY) { cmd = NULL; } else { tx = test_binder_cmd_payload(cmd); } break; case BR_FAILED_REPLY: case BR_DEAD_REPLY: if (flags & CMD_DEQUEUE_FLAG_STOP_AT_REPLY) { cmd = NULL; } break; } if (cmd && BINDER_CMDSIZE(code) > max_size) { GDEBUG("Cmd 0x%08x is too large (%d > %d)", code, BINDER_CMDSIZE(code), max_size); cmd = NULL; } else if (tx && !tx->cookie) { /* We use cookie to store tid of the sender */ tx->cookie = (cmd->tid > 0) ? cmd->tid : tid; } break; } prev = cmd; } if (cmd) { TestBinderCmd* next = cmd->next; guint8 pong = 0; if (prev) { if (!(prev->next = next)) { node->cmd_last = prev; } } else { if (!(node->cmd_first = next)) { node->cmd_last = NULL; } } g_assert_cmpint(read(node->public_fd, &pong, 1), > ,0); } return cmd; } static void test_binder_node_fix_affinity( TestBinderNode* node, TEST_BR_THREAD mark) { const int tid = gettid(); TestBinderCmd* cmd; /* Lock */ test_binder_node_lock(node); for (cmd = node->cmd_first; cmd; cmd = cmd->next) { if (cmd->tid == mark) { const guint32 code = test_binder_cmd_code(cmd); cmd->tid = tid; GDEBUG("Cmd 0x%08x affinity changed to %d", code, tid); if (code == BR_REPLY_64 || code == BR_TRANSACTION_64) { BinderTransactionData64* tx = test_binder_cmd_payload(cmd); g_assert(!tx->cookie); tx->cookie = tid; } } } test_binder_node_unlock(node); /* Unlock */ } static void test_binder_short_wait() { usleep(100000); /* 100 ms */ } static gboolean test_binder_node_ignore_dead_object( TestBinderNode* node) { gboolean ignore; /* Lock */ test_binder_node_lock(node); if (node->ignore_dead_object > 0) { node->ignore_dead_object--; ignore = TRUE; } else { ignore = FALSE; } test_binder_node_unlock(node); /* Unlock */ return ignore; } static void test_binder_node_br_dead_object_64( TestBinderNode* node, guint64 handle) { guint8 br[BINDER_CMDSIZE(BR_DEAD_BINDER_64)]; guint32* code = (guint32*)br; guint64* payload = (guint64*)(code + 1); *code = BR_DEAD_BINDER_64; *payload = handle; test_binder_node_queue_cmd_data(node, ANY_THREAD, br); } static void test_binder_local_object_gone( gpointer data, GObject* obj) { TestBinderNode* node = data; gssize handle = -1; /* * TestBinderNode must be alive because if gets detroyed after * all local objects are gone. */ /* Lock */ test_binder_node_lock(node); GDEBUG("Object %p is gone", obj); if (g_hash_table_contains(node->object_map, obj)) { gpointer key = g_hash_table_lookup(node->object_map, obj); handle = GPOINTER_TO_SIZE(key); g_hash_table_remove(node->handle_map, key); g_hash_table_remove(node->object_map, obj); } test_binder_node_unlock(node); /* Unlock */ if (handle >= 0) { test_binder_node_br_dead_object_64(node, handle); } test_binder_node_unref(node); } static guint test_binder_register_object_locked( TestBinderNode* node, GBinderLocalObject* obj, guint h) { g_assert(G_TYPE_CHECK_INSTANCE_TYPE(obj, GBINDER_TYPE_LOCAL_OBJECT)); g_assert(!g_hash_table_contains(node->object_map, obj)); g_assert(!g_hash_table_contains(node->handle_map, GINT_TO_POINTER(h))); if (h == AUTO_HANDLE) { h = g_atomic_int_add(&last_auto_handle, 1) + 1; while (g_hash_table_contains(node->handle_map, GINT_TO_POINTER(h)) || g_hash_table_contains(node->object_map, GINT_TO_POINTER(h))) { h = g_atomic_int_add(&last_auto_handle, 1) + 1; } } else if (last_auto_handle < h) { /* Avoid re-using handles, to make debugging easier */ g_atomic_int_set(&last_auto_handle, h); } GDEBUG("Object %p <=> handle %u %s", obj, h, node->name); g_hash_table_insert(node->handle_map, GINT_TO_POINTER(h), obj); g_hash_table_insert(node->object_map, obj, GINT_TO_POINTER(h)); g_object_weak_ref(G_OBJECT(obj), test_binder_local_object_gone, test_binder_node_ref(node)); return h; } static guint64 test_binder_node_object_to_handle_locked( TestBinderNode* node, guint64 obj) { gpointer key = GSIZE_TO_POINTER(obj); if (g_hash_table_contains(node->object_map, key)) { gpointer value = g_hash_table_lookup(node->object_map, key); guint64 handle = GPOINTER_TO_SIZE(value); GDEBUG("Object %p => handle %u %s", key, (guint) handle, node->name); return handle; } else if (key) { GDEBUG("Auto-registering object %p %s", key, node->name); return test_binder_register_object_locked(node, key, AUTO_HANDLE); } else { GDEBUG("Unexpected object %p %s", key, node->name); return 0; } } static guint64 test_binder_node_handle_to_object_locked( TestBinderNode* node, guint64 handle) { gpointer key = GSIZE_TO_POINTER(handle); if (g_hash_table_contains(node->handle_map, key)) { gpointer obj = g_hash_table_lookup(node->handle_map, key); GDEBUG("Handle %u => object %p %s", (guint) handle, obj, node->name); return GPOINTER_TO_SIZE(obj); } return 0; } static guint64 test_binder_node_handle_to_object( TestBinderNode* node, guint64 handle) { guint64 obj; /* Lock */ test_binder_node_lock(node); obj = test_binder_node_handle_to_object_locked(node, handle); test_binder_node_unlock(node); /* Unlock */ return obj; } static void test_binder_node_fixup_handles_64( TestBinderNode* node, BinderTransactionData64* tx) { guint i; guint8* data_buffer = gutil_memdup (GSIZE_TO_POINTER(tx->data_buffer), tx->data_size); guint64* data_offsets = gutil_memdup (GSIZE_TO_POINTER(tx->data_offsets), tx->offsets_size); /* Lock */ test_binder_node_lock(node); for (i = 0; i < tx->offsets_size/sizeof(guint64); i++) { guint32* obj_ptr = (guint32*)(data_buffer + data_offsets[i]); if (*obj_ptr == BINDER_TYPE_BINDER) { BinderObject64* object = (BinderObject64*) obj_ptr; if (object->ptr) { object->type = BINDER_TYPE_HANDLE; object->handle = test_binder_node_object_to_handle_locked(node, object->ptr); } } else if (*obj_ptr == BINDER_TYPE_PTR) { BinderBuffer64* buffer = (BinderBuffer64*) obj_ptr; if (buffer->buffer) { void* copy = gutil_memdup (GSIZE_TO_POINTER(buffer->buffer), buffer->length); if (buffer->flags & BINDER_BUFFER_FLAG_HAS_PARENT) { /* Fix pointer from the parent buffer */ BinderBuffer64* parent_buffer = (void*) (data_buffer + data_offsets[buffer->parent]); guint64* parent_ptr = GSIZE_TO_POINTER (parent_buffer->buffer + buffer->parent_offset); g_assert(parent_buffer->type == BINDER_TYPE_PTR); g_assert(*parent_ptr == buffer->buffer); *parent_ptr = GPOINTER_TO_SIZE(copy); } buffer->buffer = GPOINTER_TO_SIZE(copy); g_hash_table_replace(node->destroy_map, copy, NULL); } } else if (*obj_ptr != BINDER_TYPE_HANDLE) { GDEBUG("Unexpected object type 0x%08x", *obj_ptr); } } g_hash_table_replace(node->destroy_map, data_buffer, NULL); g_hash_table_replace(node->destroy_map, data_offsets, NULL); test_binder_node_unlock(node); /* Unlock */ tx->data_buffer = GPOINTER_TO_SIZE(data_buffer); tx->data_offsets = GPOINTER_TO_SIZE(data_offsets); } static void test_binder_node_br_clear_death_notification_done_64( TestBinderNode* node, guint64 cookie) { guint8 br[BINDER_CMDSIZE(BR_CLEAR_DEATH_NOTIFICATION_DONE_64)]; guint32* code = (guint32*)br; guint64* payload = (guint64*)(code + 1); *code = BR_CLEAR_DEATH_NOTIFICATION_DONE_64; *payload = cookie; test_binder_node_queue_cmd_data(node, gettid(), br); } static void test_binder_node_bc_free_buffer_64( TestBinderNode* node, const guint64* payload) { gpointer ptr = GSIZE_TO_POINTER(*payload); if (ptr) { GDestroyNotify destroy; /* Lock */ test_binder_node_lock(node); destroy = g_hash_table_lookup(node->destroy_map, ptr); g_hash_table_remove(node->destroy_map, ptr); if (!destroy) { destroy = g_free; } test_binder_node_unlock(node); /* Unlock */ destroy(ptr); } } static void test_binder_node_bc_clear_death_notification_64( TestBinderNode* node, const BinderHandleCookie64* bc) { test_binder_node_br_clear_death_notification_done_64(node, bc->cookie); } static void test_binder_node_bc_transaction_64( TestBinderNode* node, const BinderTransactionData64* bc) { const int tid = gettid(); guint64 obj = test_binder_node_handle_to_object(node, bc->target.handle); test_binder_node_fix_affinity(node, TX_THREAD); if (obj) { guint8 br[BINDER_CMDSIZE(BR_TRANSACTION_64)]; guint32* code = (guint32*)br; BinderTransactionData64* tx = (BinderTransactionData64*)(code + 1); *code = BR_TRANSACTION_64; *tx = *bc; g_assert(!tx->cookie); /* We use cookie to store tid of the sender */ tx->cookie = tid; tx->sender_pid = getpid(); tx->sender_euid = geteuid(); tx->target.ptr = obj; test_binder_node_fixup_handles_64(node, tx); test_binder_node_queue_cmd_data(node, ANY_THREAD, br); } else if (test_binder_node_ignore_dead_object(node)) { GDEBUG("No object for handle %u (expected)", bc->target.handle); } else { const guint32 br_dead_reply = BR_DEAD_REPLY; GDEBUG("No object for handle %u", bc->target.handle); test_binder_node_queue_cmd_data(node, tid, &br_dead_reply); } } static void test_binder_node_bc_reply_64( TestBinderNode* node, const BinderTransactionData64* bc) { const guint32 br_noop = BR_NOOP; const guint32 br_transaction_complete = BR_TRANSACTION_COMPLETE; guint8 br_reply[BINDER_CMDSIZE(BR_REPLY_64)]; guint32* code = (guint32*)br_reply; BinderTransactionData64* tx = (BinderTransactionData64*)(code + 1); GUtilIntArray* tx_stack = g_private_get(&test_tx_stack); int tid; *code = BR_REPLY_64; *tx = *bc; tx->sender_pid = getpid(); tx->sender_euid = geteuid(); test_binder_node_fixup_handles_64(node, tx); /* Pop tid of the thread waiting for the completion */ g_assert(tx_stack); tid = tx_stack->data[tx_stack->count - 1]; GDEBUG("Reply thread %d", tid); if (tx_stack->count == 1) { /* This thread is done with its transactions */ gutil_int_array_free(tx_stack, TRUE); g_private_set(&test_tx_stack, NULL); } else { gutil_int_array_set_count(tx_stack, tx_stack->count - 1); } /* Queue BR_TRANSACTION_COMPLETE, BR_NOOP and the reply */ /* Lock */ test_binder_node_lock(node); test_binder_node_queue_cmd_data_locked(node, tid, &br_transaction_complete); test_binder_node_queue_cmd_data_locked(node, tid, &br_noop); test_binder_node_queue_cmd_data_locked(node, tid, br_reply); test_binder_node_unlock(node); /* Unlock */ } static void test_binder_node_bc_acquire_release( TestBinderNode* node, guint32 code, const guint32* handle) { guint64 ptr; g_assert_cmpuint(_IOC_SIZE(code), == ,sizeof(BinderPtrCookie64)); /* Lock */ test_binder_node_lock(node); ptr = test_binder_node_handle_to_object_locked(node, *handle); if (ptr) { guint8 br[sizeof(guint32) + sizeof(BinderPtrCookie64)]; BinderPtrCookie64* data = (void*)(br + sizeof(code)); memcpy(br, &code, sizeof(code)); memset(data, 0, sizeof(*data)); data->ptr = ptr; /* These commands are not tied to a particular thread */ test_binder_node_queue_cmd_data_locked(node, ANY_THREAD, br); } else { /* This may actually be expected in some weird unit tests */ GDEBUG("Invalid handle %u %s", *handle, node->name); } test_binder_node_unlock(node); /* Unlock */ } static void test_binder_node_handle_cmd_64( TestBinderNode* node, const guint32* cmd) { const guint32 code = cmd[0]; const void* payload = cmd + 1; const int tid = gettid(); switch (code) { case BC_ENTER_LOOPER: /* Lock */ test_binder_node_lock(node); g_assert(!gutil_int_array_contains(node->loopers, tid)); gutil_int_array_append(node->loopers, tid); test_binder_node_unlock(node); /* Unlock */ GDEBUG("Thread %d is a looper", tid); break; case BC_EXIT_LOOPER: /* Lock */ test_binder_node_lock(node); g_assert(gutil_int_array_remove(node->loopers, tid)); test_binder_node_unlock(node); /* Unlock */ GDEBUG("Thread %d is no longer a looper", tid); break; case BC_CLEAR_DEATH_NOTIFICATION_64: test_binder_node_bc_clear_death_notification_64(node, payload); break; case BC_FREE_BUFFER_64: test_binder_node_bc_free_buffer_64(node, payload); break; case BC_TRANSACTION_64: case BC_TRANSACTION_SG_64: test_binder_node_bc_transaction_64(node, payload); break; case BC_REPLY_64: case BC_REPLY_SG_64: test_binder_node_bc_reply_64(node, payload); break; case BC_ACQUIRE: test_binder_node_bc_acquire_release(node, BR_ACQUIRE_64, payload); break; case BC_RELEASE: test_binder_node_bc_acquire_release(node, BR_RELEASE_64, payload); break; case BC_ACQUIRE_DONE_64: case BC_DEAD_BINDER_DONE: case BC_REQUEST_DEATH_NOTIFICATION_64: /* Ignore these for now */ break; default: GDEBUG("Ignoring cmd 0x%08x", code); break; } } static void test_binder_node_handle_br_transaction_64_locked( TestBinderNode* node, const BinderTransactionData64* tx) { /* We use cookie to store tid of the sender */ const int sender = (int) tx->cookie; g_assert(sender); GDEBUG("Thread %d receives %stransaction from %d", gettid(), (tx->flags & TF_ONE_WAY) ? "one-way " : "", sender); if (tx->flags & TF_ONE_WAY) { const guint32 br_noop = BR_NOOP; const guint32 br_transaction_complete = BR_TRANSACTION_COMPLETE; /* Let the sender know that transaction has been delivered */ test_binder_node_queue_cmd_data_locked(node, sender, &br_noop); test_binder_node_queue_cmd_data_locked(node, sender, &br_transaction_complete); } else { GUtilIntArray* tx_stack = g_private_get(&test_tx_stack); if (!tx_stack) { g_private_set(&test_tx_stack, tx_stack = gutil_int_array_new()); } /* Push sender's tid to the transaction stack and wait for reply */ gutil_int_array_append(tx_stack, sender); } } static int test_binder_io_handle_write_read_64( TestBinderNode* node, void* data) { BinderWriteRead64* wr = data; gssize bytes_left = wr->write_size - wr->write_consumed; guint8* read_ptr = GSIZE_TO_POINTER(wr->read_buffer + wr->read_consumed); const guint8* write_ptr = GSIZE_TO_POINTER(wr->write_buffer + wr->write_consumed); int read_total = 0, avail = wr->read_size - wr->read_consumed; CMD_DEQUEUE_FLAGS flags = CMD_DEQUEUE_FLAGS_NONE; TestBinderCmd* cmd; /* Write */ while (bytes_left >= sizeof(guint32)) { const guint* cmd = (guint32*)write_ptr; const gsize cmdsize = BINDER_CMDSIZE(*cmd); /* Not expecting partial commands in the buffer */ g_assert(bytes_left >= cmdsize); test_binder_node_handle_cmd_64(node, cmd); wr->write_consumed += cmdsize; bytes_left -= cmdsize; write_ptr += cmdsize; } /* Read */ /* Lock */ test_binder_node_lock(node); while (avail && (cmd = test_binder_node_dequeue_cmd_locked(node, avail, flags))) { const int nbytes = test_binder_cmd_size(cmd); const guint32 code = test_binder_cmd_code(cmd); g_assert_cmpint(nbytes, <= ,avail); switch (code) { case BR_TRANSACTION_64: flags |= CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION; test_binder_node_handle_br_transaction_64_locked(node, test_binder_cmd_payload(cmd)); break; case BR_TRANSACTION_COMPLETE: flags |= CMD_DEQUEUE_FLAG_STOP_AT_TRANSACTION_COMPLETE; break; case BR_REPLY_64: case BR_FAILED_REPLY: case BR_DEAD_REPLY: flags |= CMD_DEQUEUE_FLAG_STOP_AT_REPLY; break; } memcpy(read_ptr, cmd->data, nbytes); wr->read_consumed += nbytes; read_total += nbytes; avail -= nbytes; read_ptr += nbytes; test_binder_cmd_free(cmd); } test_binder_node_unlock(node); /* Unlock */ if (!read_total) { /* Nothing was read */ test_binder_short_wait(); } return 0; } static const TestBinderIo test_binder_io_64 = { .version = 8, .write_read_request = _IOWR('b', 1, BinderWriteRead64), .handle_write_read = test_binder_io_handle_write_read_64 }; static int test_binder_ioctl_version( TestBinderNode* node, int* version) { *version = node->io->version; return 0; } static void test_io_destroy_none( gpointer data) { GDEBUG("Not freeing %p", data); } void test_binder_set_destroy( int fd, gpointer ptr, GDestroyNotify destroy) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); if (node) { /* Lock */ test_binder_node_lock(node); g_hash_table_replace(node->destroy_map, ptr, destroy ? destroy : test_io_destroy_none); test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); } } static void test_binder_push_data( int fd, TEST_BR_THREAD dest, const void* data) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); if (node) { test_binder_node_queue_cmd_data(node, (dest == THIS_THREAD) ? gettid() : dest, data); test_binder_node_unref(node); } } static void test_binder_push_ptr_cookie( int fd, TEST_BR_THREAD dest, guint32 cmd, void* ptr) { guint8 buf[sizeof(guint32) + sizeof(BinderPtrCookie64)]; BinderPtrCookie64* data = (void*)(buf + sizeof(cmd)); g_assert_cmpuint(_IOC_SIZE(cmd), == ,sizeof(BinderPtrCookie64)); memcpy(buf, &cmd, sizeof(cmd)); memset(data, 0, sizeof(*data)); data->ptr = GPOINTER_TO_SIZE(ptr); test_binder_push_data(fd, dest, buf); } void test_binder_br_noop( int fd, TEST_BR_THREAD dest) { guint32 cmd = BR_NOOP; test_binder_push_data(fd, dest, &cmd); } void test_binder_br_increfs( int fd, TEST_BR_THREAD dest, void* ptr) { test_binder_push_ptr_cookie(fd, dest, BR_INCREFS_64, ptr); } void test_binder_br_acquire( int fd, TEST_BR_THREAD dest, void* ptr) { test_binder_push_ptr_cookie(fd, dest, BR_ACQUIRE_64, ptr); } void test_binder_br_release( int fd, TEST_BR_THREAD dest, void* ptr) { test_binder_push_ptr_cookie(fd, dest, BR_RELEASE_64, ptr); } void test_binder_br_decrefs( int fd, TEST_BR_THREAD dest, void* ptr) { test_binder_push_ptr_cookie(fd, dest, BR_DECREFS_64, ptr); } void test_binder_br_transaction_complete( int fd, TEST_BR_THREAD dest) { guint32 cmd = BR_TRANSACTION_COMPLETE; test_binder_push_data(fd, dest, &cmd); } void test_binder_br_dead_binder( int fd, TEST_BR_THREAD dest, guint handle) { const guint64 handle64 = handle; guint32 br[3]; br[0] = BR_DEAD_BINDER_64; memcpy(br + 1, &handle64, sizeof(handle64)); test_binder_push_data(fd, dest, br); } void test_binder_br_dead_binder_obj( int fd, GBinderLocalObject* obj) { if (obj) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); g_assert(node); /* Lock */ test_binder_node_lock(node); if (g_hash_table_contains(node->object_map, obj)) { guint32 br[3]; const guint64 handle64 = GPOINTER_TO_SIZE (g_hash_table_lookup(node->object_map, obj)); br[0] = BR_DEAD_BINDER_64; memcpy(br + 1, &handle64, sizeof(handle64)); test_binder_node_queue_cmd_data_locked(node, ANY_THREAD, br); } test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); } } void test_binder_br_dead_reply( int fd, TEST_BR_THREAD dest) { guint32 cmd = BR_DEAD_REPLY; test_binder_push_data(fd, dest, &cmd); } void test_binder_br_failed_reply( int fd, TEST_BR_THREAD dest) { guint32 cmd = BR_FAILED_REPLY; test_binder_push_data(fd, dest, &cmd); } static void test_binder_fill_transaction_data( BinderTransactionData64* tr, const BinderTarget64* target, guint32 code, const GByteArray* bytes) { memset(tr, 0, sizeof(*tr)); tr->target = *target; tr->code = code; tr->data_size = bytes ? bytes->len : 0; tr->sender_pid = getpid(); tr->sender_euid = geteuid(); /* This memory should eventually get deallocated with BC_FREE_BUFFER_64 */ tr->data_buffer = (gsize)gutil_memdup(bytes ? (void*) bytes->data : (void*) "", tr->data_size); } void test_binder_br_transaction( int fd, TEST_BR_THREAD dest, void* ptr, guint32 code, const GByteArray* bytes) { guint32 cmd = BR_TRANSACTION_64; guint8 br[sizeof(guint32) + sizeof(BinderTransactionData64)]; BinderTarget64 target; memset(&target, 0, sizeof(target)); target.ptr = GPOINTER_TO_SIZE(ptr); memset(br, 0, sizeof(br)); memcpy(br, &cmd, sizeof(cmd)); test_binder_fill_transaction_data((void*)(br + sizeof(cmd)), &target, code, bytes); test_binder_push_data(fd, dest, br); } void test_binder_br_reply( int fd, TEST_BR_THREAD dest, guint32 handle, guint32 code, const GByteArray* bytes) { guint32 cmd = BR_REPLY_64; guint8 br[sizeof(guint32) + sizeof(BinderTransactionData64)]; BinderTarget64 target; memset(&target, 0, sizeof(target)); target.handle = handle; memset(br, 0, sizeof(br)); memcpy(br, &cmd, sizeof(cmd)); test_binder_fill_transaction_data((void*)(br + sizeof(cmd)), &target, code, bytes); test_binder_push_data(fd, dest, br); } void test_binder_br_reply_status( int fd, TEST_BR_THREAD dest, gint32 status) { guint32 cmd = BR_REPLY_64; guint8 br[sizeof(guint32) + sizeof(BinderTransactionData64)]; BinderTransactionData64* tr = (void*)(br + sizeof(cmd)); memset(br, 0, sizeof(br)); memcpy(br, &cmd, sizeof(cmd)); tr->flags = TF_STATUS_CODE; tr->data_size = sizeof(status); /* This memory should eventually get deallocated with BC_FREE_BUFFER_64 */ tr->data_buffer = (gsize) gutil_memdup(&status, sizeof(status)); test_binder_push_data(fd, dest, br); } int test_binder_handle( int fd, GBinderLocalObject* obj) { int h = -1; if (obj) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); g_assert(node); /* Lock */ test_binder_node_lock(node); if (g_hash_table_contains(node->object_map, obj)) { h = GPOINTER_TO_INT(g_hash_table_lookup(node->object_map, obj)); } test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); } return h; } GBinderLocalObject* test_binder_object( int fd, guint handle) { GBinderLocalObject* obj = NULL; TestBinderNode* node = test_binder_node_ref_from_fd(fd); if (node) { gpointer key = GSIZE_TO_POINTER(handle); /* Lock */ test_binder_node_lock(node); if (g_hash_table_contains(node->handle_map, key)) { obj = gbinder_local_object_ref (g_hash_table_lookup(node->handle_map, key)); } test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); } return obj; } guint test_binder_register_object( int fd, GBinderLocalObject* obj, guint h) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); g_assert(node); /* Lock */ test_binder_node_lock(node); h = test_binder_register_object_locked(node, obj, h); test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); return h; } void test_binder_ignore_dead_object( int fd) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); g_assert(node); /* Lock */ test_binder_node_lock(node); node->ignore_dead_object++; test_binder_node_unlock(node); /* Unlock */ test_binder_node_unref(node); } static void test_binder_node_unregister_objects( TestBinderNode* node, GUtilIntArray* handles) { GHashTableIter it; gpointer handle, obj; /* Lock */ test_binder_node_lock(node); g_hash_table_iter_init(&it, node->handle_map); while (g_hash_table_iter_next(&it, &handle, &obj)) { gutil_int_array_append(handles, GPOINTER_TO_INT(handle)); g_hash_table_remove(node->object_map, obj); g_hash_table_iter_remove(&it); } test_binder_node_unlock(node); /* Unlock */ } void test_binder_unregister_objects( int fd) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); GUtilIntArray* handles = gutil_int_array_new(); guint i; g_assert(node); test_binder_node_unregister_objects(node, handles); for (i = 0; i < handles->count; i++) { test_binder_node_br_dead_object_64(node, handles->data[i]); } test_binder_node_unref(node); gutil_int_array_free(handles, TRUE); } void test_binder_exit_wait( const TestOpt* opt, GMainLoop* loop) { gbinder_ipc_exit(); G_LOCK(test_binder); if (test_path_map) { g_assert(!test_binder_exit_loop); if (loop) { g_main_loop_ref(loop); } else { loop = g_main_loop_new(NULL, FALSE); } test_binder_exit_loop = loop; GDEBUG("Waiting for all nodes to get destroyed..."); G_UNLOCK(test_binder); test_run(opt, loop); G_LOCK(test_binder); g_assert(!test_path_map); test_binder_exit_loop = NULL; g_main_loop_unref(loop); } G_UNLOCK(test_binder); test_binder_destroy_data_flush(); } int gbinder_system_open( const char* path, int flags) { static const char path_prefix[] = "/dev/"; if (path && g_str_has_prefix(path, path_prefix)) { TestBinderNode* node; TestBinderFd* binder_fd; int fd; G_LOCK(test_binder); node = test_path_map ? g_hash_table_lookup(test_path_map, path) : NULL; if (node) { test_binder_node_ref(node); } else { node = test_binder_node_new(path, &test_binder_io_64); if (!test_path_map) { test_path_map = g_hash_table_new(g_str_hash, g_str_equal); } g_hash_table_replace(test_path_map, node->path, node); } binder_fd = test_binder_fd_new(node); test_binder_node_unref(node); /* Drop temporary ref */ if (!test_fd_map) { test_fd_map = g_hash_table_new(g_direct_hash, g_direct_equal); } fd = binder_fd->fd; g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd), binder_fd); G_UNLOCK(test_binder); return fd; } else { errno = ENOENT; return -1; } } int gbinder_system_close( int fd) { int ret; TestBinderFd* binder_fd; G_LOCK(test_binder); binder_fd = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd)); if (binder_fd) { GDEBUG("Closed fd %d %s", fd, binder_fd->node->path); g_assert(g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd))); if (!g_hash_table_size(test_fd_map)) { g_hash_table_unref(test_fd_map); test_fd_map = NULL; } ret = 0; } else { errno = EBADF; ret = -1; } G_UNLOCK(test_binder); if (binder_fd) { test_binder_fd_free(binder_fd); } return ret; } int gbinder_system_ioctl( int fd, int request, void* data) { TestBinderNode* node = test_binder_node_ref_from_fd(fd); if (node) { int ret; const TestBinderIo* io = node->io; errno = 0; switch (request) { case BINDER_VERSION: ret = test_binder_ioctl_version(node, data); break; case BINDER_SET_MAX_THREADS: ret = 0; break; default: if (request == io->write_read_request) { ret = io->handle_write_read(node, data); } else { errno = EINVAL; ret = -1; } break; } test_binder_node_unref(node); return ret; } else { errno = EBADF; return -1; } } void* gbinder_system_mmap( size_t length, int prot, int flags, int fd) { return g_malloc(length); } int gbinder_system_munmap( void* addr, size_t length) { g_free(addr); return 0; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/common/test_binder.h000066400000000000000000000101031526127616700206350ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TEST_BINDER_H #define TEST_BINDER_H #include "test_common.h" #define B_TYPE_LARGE 0x85 #define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', B_TYPE_LARGE) #define BINDER_TYPE_HANDLE GBINDER_FOURCC('s', 'h', '*', B_TYPE_LARGE) #define BINDER_TYPE_PTR GBINDER_FOURCC('p', 't', '*', B_TYPE_LARGE) #define BUFFER_OBJECT_SIZE_32 (24) #define BUFFER_OBJECT_SIZE_64 (40) #define BINDER_OBJECT_SIZE_32 (16) #define BINDER_OBJECT_SIZE_64 (24) typedef enum test_br_thread { THIS_THREAD = -3, LOOPER_THREAD = -2, TX_THREAD = -1, ANY_THREAD = 0 } TEST_BR_THREAD; void test_binder_br_noop( int fd, TEST_BR_THREAD dest); void test_binder_br_increfs( int fd, TEST_BR_THREAD dest, void* ptr); void test_binder_br_acquire( int fd, TEST_BR_THREAD dest, void* ptr); void test_binder_br_release( int fd, TEST_BR_THREAD dest, void* ptr); void test_binder_br_decrefs( int fd, TEST_BR_THREAD dest, void* ptr); void test_binder_br_transaction_complete( int fd, TEST_BR_THREAD dest); void test_binder_br_dead_binder( int fd, TEST_BR_THREAD dest, guint handle); void test_binder_br_dead_binder_obj( int fd, GBinderLocalObject* obj); void test_binder_br_dead_reply( int fd, TEST_BR_THREAD dest); void test_binder_br_failed_reply( int fd, TEST_BR_THREAD dest); void test_binder_br_transaction( int fd, TEST_BR_THREAD dest, void* target, guint32 code, const GByteArray* bytes); void test_binder_br_reply( int fd, TEST_BR_THREAD dest, guint32 handle, guint32 code, const GByteArray* bytes); void test_binder_br_reply_status( int fd, TEST_BR_THREAD dest, gint32 status); void test_binder_ignore_dead_object( int fd); int test_binder_handle( int fd, GBinderLocalObject* obj); GBinderLocalObject* test_binder_object( int fd, guint handle) G_GNUC_WARN_UNUSED_RESULT; /* Need to unref */ guint test_binder_register_object( int fd, GBinderLocalObject* obj, guint handle); #define AUTO_HANDLE ((guint)-1) void test_binder_unregister_objects( int fd); void test_binder_set_destroy( int fd, gpointer ptr, GDestroyNotify destroy); void test_binder_exit_wait( const TestOpt* opt, GMainLoop* loop); #endif /* TEST_BINDER_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/common/test_common.h000066400000000000000000000110761526127616700206740ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2023 Slava Monich * Copyright (C) 2018-2021 Jolla Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TEST_COMMON_H #define TEST_COMMON_H #include #define TEST_FLAG_DEBUG (0x01) typedef struct test_opt { int flags; } TestOpt; typedef struct test_config { const char* default_config_dir; const char* default_config_file; char* config_dir; char* non_existent_config_file; } TestConfig; /* Should be invoked after g_test_init */ void test_init( TestOpt* opt, int argc, char* argv[]); /* Creates empty test config dir */ void test_config_init( TestConfig* config, const char* template); void test_config_cleanup( TestConfig* config); /* Run loop with a timeout */ void test_run( const TestOpt* opt, GMainLoop* loop); /* Quits the event loop on the next iteration */ void test_quit_later( GMainLoop* loop); /* Quits the event loop after n iterations */ void test_quit_later_n( GMainLoop* loop, guint n); /* * Makes sure that we own the context for the entire duration of the test. * That prevents many race conditions - all callbacks that are supposed to * be invoked on the main thread, are actually invoked on the main thread * (rather than a random worker thread which happens to acquire the context). */ void test_run_in_context( const TestOpt* opt, GTestFunc func); void test_run_in_context_param( const TestOpt* opt, GTestDataFunc func, gconstpointer param); #define TEST_TIMEOUT_SEC (20) /* Helper macros */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN # define TEST_INT8_BYTES_4(v) \ (guint8)(v), 0, 0, 0 # define TEST_INT16_BYTES_4(v) \ (guint8)(v), (guint8)((v) >> 8), 0, 0 # define TEST_INT16_BYTES(v) \ (guint8)(v), (guint8)((v) >> 8) # define TEST_INT32_BYTES(v) \ (guint8)(v), (guint8)((v) >> 8), \ (guint8)((v) >> 16), (guint8)((v) >> 24) # define TEST_INT64_BYTES(v) \ (guint8)(v), (guint8)((v) >> 8), \ (guint8)((v) >> 16), (guint8)((v) >> 24), \ (guint8)(((guint64)(v)) >> 32), (guint8)(((guint64)(v)) >> 40), \ (guint8)(((guint64)(v)) >> 48), (guint8)(((guint64)(v)) >> 56) #elif G_BYTE_ORDER == G_BIG_ENDIAN # define TEST_INT8_BYTES_4(v) \ 0, 0, 0, (guint8)(v) # define TEST_INT16_BYTES_4(v) \ 0, 0, (guint8)((v) >> 8), (guint8)(v) # define TEST_INT16_BYTES(v) \ (guint8)((v) >> 8), (guint8)(v) # define TEST_INT32_BYTES(v) \ (guint8)((v) >> 24), (guint8)((v) >> 16), \ (guint8)((v) >> 8), (guint8)(v) # define TEST_INT64_BYTES(v) \ (guint8)(((guint64)(v)) >> 56), (guint8)(((guint64)(v)) >> 48), \ (guint8)(((guint64)(v)) >> 40), (guint8)(((guint64)(v)) >> 32), \ (guint8)((v) >> 24), (guint8)((v) >> 16), \ (guint8)((v) >> 8), (guint8)(v) #else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ #error unknown ENDIAN type #endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ #define TEST_ARRAY_AND_COUNT(a) a, G_N_ELEMENTS(a) #define TEST_ARRAY_AND_SIZE(a) a, sizeof(a) #endif /* TEST_COMMON_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/common/test_main.c000066400000000000000000000153731526127616700203270ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2018-2020 Jolla Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_config.h" #include "gbinder_rpc_protocol.h" #include #include typedef struct test_quit_later_data{ GMainLoop* loop; guint n; } TestQuitLaterData; typedef struct test_context_data{ GMainLoop* loop; GTestDataFunc func; gconstpointer param; } TestContextData; static void test_quit_later_n_free( gpointer user_data) { TestQuitLaterData* data = user_data; g_main_loop_unref(data->loop); g_free(data); } static gboolean test_quit_later_n_func( gpointer user_data) { TestQuitLaterData* data = user_data; if (data->n > 0) { data->n--; return G_SOURCE_CONTINUE; } else { g_main_loop_quit(data->loop); return G_SOURCE_REMOVE; } } void test_quit_later_n( GMainLoop* loop, guint n) { TestQuitLaterData* data = g_new0(TestQuitLaterData, 1); data->loop = g_main_loop_ref(loop); data->n = n; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_quit_later_n_func, data, test_quit_later_n_free); } static gboolean test_quit_later_cb( gpointer data) { g_main_loop_quit((GMainLoop*)data); return G_SOURCE_REMOVE; } void test_quit_later( GMainLoop* loop) { g_idle_add(test_quit_later_cb, loop); } static void test_run_in_context_cb( gconstpointer param) { ((GTestFunc)param)(); } void test_run_in_context( const TestOpt* opt, GTestFunc func) { test_run_in_context_param(opt, test_run_in_context_cb, func); } static gboolean test_run_in_context_param_cb( gpointer data) { const TestContextData* test = data; test->func(test->param); g_main_loop_quit(test->loop); return G_SOURCE_REMOVE; } void test_run_in_context_param( const TestOpt* opt, GTestDataFunc func, gconstpointer param) { TestContextData test; test.loop = g_main_loop_new(NULL, FALSE); test.func = func; test.param = param; /* * This makes sure that we own the context for the entire duration * of the test. That prevents many race conditions - all callbacks * that are supposed to be invoked on the main thread, are actually * invoked on the main thread (rather than a random worker thread * which happens to acquire the context). */ g_idle_add(test_run_in_context_param_cb, &test); test_run(opt, test.loop); g_main_loop_unref(test.loop); } void test_run( const TestOpt* opt, GMainLoop* loop) { if (opt->flags & TEST_FLAG_DEBUG) { g_main_loop_run(loop); } else { static guint depth; /* SIGALRM terminates the process even if the main thread is stuck. * Nested loops must neither extend nor cancel the outer timeout. */ if (!depth++) { alarm(TEST_TIMEOUT_SEC); } g_main_loop_run(loop); if (!--depth) { alarm(0); } } } void test_init( TestOpt* opt, int argc, char* argv[]) { const char* sep1; const char* sep2; int i; memset(opt, 0, sizeof(*opt)); for (i=1; iflags |= TEST_FLAG_DEBUG; } else if (!strcmp(arg, "-v")) { GTestConfig* config = (GTestConfig*)g_test_config_vars; config->test_verbose = TRUE; } else { GWARN("Unsupported command line option %s", arg); } } /* Setup logging */ sep1 = strrchr(argv[0], '/'); sep2 = strrchr(argv[0], '\\'); gutil_log_default.name = (sep1 && sep2) ? (MAX(sep1, sep2) + 1) : sep1 ? (sep1 + 1) : sep2 ? (sep2 + 1) : argv[0]; gutil_log_default.level = g_test_verbose() ? GLOG_LEVEL_VERBOSE : GLOG_LEVEL_NONE; } /* * Unit tests shouldn't depend on the system gbinder config. This is * achieved by pointing gbinder_config_file to a non-existent file * and gbinder_config_dir to an empty directory (where individual * tests may create their own config files when they need it) */ void test_config_init( TestConfig* config, const char* template) { config->config_dir = g_dir_make_tmp("gbinder-test-driver-XXXXXX", NULL); g_assert(config->config_dir); config->non_existent_config_file = g_build_filename(config->config_dir, "non-existent.conf", NULL); /* Point gbinder_config_file to a non-existent file */ config->default_config_dir = gbinder_config_dir; config->default_config_file = gbinder_config_file; gbinder_config_dir = config->config_dir; gbinder_config_file = config->non_existent_config_file; /* Reset the state */ gbinder_rpc_protocol_exit(); gbinder_config_exit(); } void test_config_cleanup( TestConfig* config) { gbinder_config_dir = config->default_config_dir; gbinder_config_file = config->default_config_file; remove(config->config_dir); g_free(config->config_dir); g_free(config->non_existent_config_file); memset(config, 0, sizeof(*config)); /* Reset the state */ gbinder_rpc_protocol_exit(); gbinder_config_exit(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/common/test_servicemanager_hidl.c000066400000000000000000000435101526127616700233700ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_servicemanager_hidl.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_local_request.h" #include "gbinder_remote_request.h" #include "gbinder_remote_reply.h" #include "gbinder_remote_object.h" #include "gbinder_reader.h" #include "gbinder_writer.h" #include "gbinder_client.h" #include "gbinder_ipc.h" #include #include /*==========================================================================* * Test service manager *==========================================================================*/ #define BASE_IFACE "android.hidl.base@1.0::IBase" #define MANAGER_IFACE "android.hidl.manager@1.0::IServiceManager" #define NOTIFICATION_IFACE "android.hidl.manager@1.0::IServiceNotification" const char* const servicemanager_hidl_ifaces[] = { MANAGER_IFACE, NULL }; enum servicemanager_hidl_tx { GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, ADD_TRANSACTION, GET_TRANSPORT_TRANSACTION, LIST_TRANSACTION, LIST_BY_INTERFACE_TRANSACTION, REGISTER_FOR_NOTIFICATIONS_TRANSACTION, DEBUG_DUMP_TRANSACTION, REGISTER_PASSTHROUGH_CLIENT_TRANSACTION }; enum servicemanager_hidl_notify_tx { ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION }; typedef GBinderLocalObjectClass TestServiceManagerHidlClass; struct test_servicemanager_hidl { GBinderLocalObject parent; GHashTable* objects; GPtrArray* watchers; GMutex mutex; }; typedef struct test_servicemanager_hidl_add { TestServiceManagerHidl* manager; GBinderRemoteObject* object; char* instance; } TestServiceManagerHidlAdd; struct test_servicemanager_hidl_watcher { GBinderClient* client; char* iface; char* name; } TestServiceManagerHidlWatcher; #define THIS_TYPE test_servicemanager_hidl_get_type() #define PARENT_CLASS test_servicemanager_hidl_parent_class #define THIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), THIS_TYPE, \ TestServiceManagerHidl)) G_DEFINE_TYPE(TestServiceManagerHidl, test_servicemanager_hidl, \ GBINDER_TYPE_LOCAL_OBJECT) static void test_servicemanager_hidl_notify( TestServiceManagerHidl* self, GBinderClient* watcher, const char* iface, const char* instance, gboolean preexisting) { GBinderLocalRequest* notify = gbinder_client_new_request(watcher); GBinderWriter writer; char* iface2 = g_strdup(iface); char* instance2 = g_strdup(instance); gbinder_local_request_init_writer(notify, &writer); gbinder_writer_append_hidl_string(&writer, iface2); gbinder_writer_append_hidl_string(&writer, instance2); gbinder_writer_append_bool(&writer, preexisting); gbinder_writer_add_cleanup(&writer, g_free, iface2); gbinder_writer_add_cleanup(&writer, g_free, instance2); gbinder_client_transact(watcher, ON_REGISTRATION_TRANSACTION, GBINDER_TX_FLAG_ONEWAY, notify, NULL, NULL, NULL); gbinder_local_request_unref(notify); } static void test_servicemanager_hidl_notify_all( TestServiceManagerHidl* self, const char* iface, const char* instance, gboolean preexisting) { GPtrArray* watchers = self->watchers; guint i; /* For unit test purposes, just always notify all watchers */ for (i = 0; i < watchers->len; i++) { test_servicemanager_hidl_notify(self, watchers->pdata[i], iface, instance, preexisting); } } static void test_servicemanager_hidl_add_complete2( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { TestServiceManagerHidlAdd* add = user_data; TestServiceManagerHidl* self = add->manager; const char* instance = add->instance; g_mutex_lock(&self->mutex); /* Remove the temporary entry */ GDEBUG("Dropping '%s'", instance); g_hash_table_remove(self->objects, instance); if (reply) { GBinderReader reader; gint32 status; gbinder_remote_reply_init_reader(reply, &reader); if (gbinder_reader_read_int32(&reader, &status) && status == 0) { char** ifaces = gbinder_reader_read_hidl_string_vec(&reader); if (ifaces) { char** ptr = ifaces; while (*ptr) { const char* iface = *ptr++; char* fqinstance = g_strconcat(iface, "/", instance, NULL); /* Add permanent entries */ GDEBUG("Adding '%s'", fqinstance); g_hash_table_replace(self->objects, fqinstance, gbinder_remote_object_ref(add->object)); test_servicemanager_hidl_notify_all(self, iface, instance, FALSE); } g_strfreev(ifaces); } } } g_mutex_unlock(&self->mutex); } static void test_servicemanager_hidl_add_done( gpointer data) { TestServiceManagerHidlAdd* add = data; gbinder_remote_object_unref(add->object); g_object_unref(add->manager); g_free(add->instance); g_free(add); } static gboolean test_servicemanager_hidl_add_complete( gpointer data) { TestServiceManagerHidlAdd* add = data; GBinderClient* client = gbinder_client_new(add->object, BASE_IFACE); gbinder_client_transact(client, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, 0, NULL, test_servicemanager_hidl_add_complete2, test_servicemanager_hidl_add_done, add); gbinder_client_unref(client); return G_SOURCE_REMOVE; } /*==========================================================================* * Call handlers *==========================================================================*/ static GBinderLocalReply* test_servicemanager_hidl_get( TestServiceManagerHidl* self, GBinderRemoteRequest* req) { GBinderRemoteObject* remote_obj; GBinderLocalReply* reply = gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self)); GBinderReader reader; GBinderWriter writer; const char* ifname; const char* instance; char* fqinstance; gbinder_remote_request_init_reader(req, &reader); g_assert((ifname = gbinder_reader_read_hidl_string_c(&reader))); g_assert((instance = gbinder_reader_read_hidl_string_c(&reader))); fqinstance = g_strconcat(ifname, "/", instance, NULL); remote_obj = g_hash_table_lookup(self->objects, fqinstance); if (!remote_obj) { remote_obj = g_hash_table_lookup(self->objects, instance); } gbinder_local_reply_init_writer(reply, &writer); gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK); if (remote_obj) { GDEBUG("Found name '%s' => %p", fqinstance, remote_obj); } else { GDEBUG("Name '%s' not found", fqinstance); } gbinder_local_reply_append_remote_object(reply, remote_obj); g_free(fqinstance); return reply; } static GBinderLocalReply* test_servicemanager_hidl_add( TestServiceManagerHidl* self, GBinderRemoteRequest* req) { GBinderRemoteObject* remote_obj; GBinderLocalReply* reply = gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self)); GBinderReader reader; const char* instance; gboolean success; gbinder_remote_request_init_reader(req, &reader); instance = gbinder_reader_read_hidl_string_c(&reader); remote_obj = gbinder_reader_read_object(&reader); if (instance && remote_obj) { const char* sep = strrchr(instance, '/'); GDEBUG("Adding '%s'", instance); /* Transfer remote_obj reference to the hashtable */ g_hash_table_replace(self->objects, g_strdup(instance), remote_obj); if (sep) { /* Already know the interface */ char* iface = g_strndup(instance, sep - instance); test_servicemanager_hidl_notify_all(self, iface, sep + 1, FALSE); g_free(iface); } else { /* Query interface chain on the main thread */ TestServiceManagerHidlAdd* add = g_new(TestServiceManagerHidlAdd,1); g_object_ref(add->manager = self); add->object = gbinder_remote_object_ref(remote_obj); add->instance = g_strdup(instance); g_idle_add(test_servicemanager_hidl_add_complete, add); } success = TRUE; } else { gbinder_remote_object_unref(remote_obj); success = FALSE; } gbinder_local_reply_append_bool(reply, success); return reply; } static GBinderLocalReply* test_servicemanager_hidl_list( TestServiceManagerHidl* self, GBinderRemoteRequest* req) { GHashTableIter it; GBinderReader reader; GBinderWriter writer; GBinderLocalReply* reply = gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self)); gpointer key; char** list = NULL; gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_at_end(&reader)); g_hash_table_iter_init(&it, self->objects); while (g_hash_table_iter_next(&it, &key, NULL)) { list = gutil_strv_add(list, key); } gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, 0); gbinder_writer_append_hidl_string_vec(&writer, (const char**) list, -1); gbinder_writer_add_cleanup(&writer, (GDestroyNotify) g_strfreev, list); return reply; } static GBinderLocalReply* test_servicemanager_hidl_register_for_notifications( TestServiceManagerHidl* self, GBinderRemoteRequest* req) { GBinderRemoteObject* watcher; GBinderLocalReply* reply = gbinder_local_object_new_reply(GBINDER_LOCAL_OBJECT(self)); GBinderReader reader; const char* iface; const char* instance; gboolean success; gbinder_remote_request_init_reader(req, &reader); iface = gbinder_reader_read_hidl_string_c(&reader); instance = gbinder_reader_read_hidl_string_c(&reader); watcher = gbinder_reader_read_object(&reader); if (watcher) { GBinderClient* wc = gbinder_client_new(watcher, NOTIFICATION_IFACE); GHashTableIter it; gpointer key; GDEBUG("Registering watcher %s/%s", iface, instance); g_ptr_array_add(self->watchers, wc); gbinder_remote_object_unref(watcher); /* Client keeps the reference */ /* Send notifications for pre-existing services */ g_hash_table_iter_init(&it, self->objects); while (g_hash_table_iter_next(&it, &key, NULL)) { const char* name = key; const char* sep = strrchr(name, '/'); if (sep) { char* intf = g_strndup(name, sep - name); test_servicemanager_hidl_notify(self, wc, intf, sep + 1, TRUE); g_free(intf); } } success = TRUE; } else { success = FALSE; } gbinder_local_reply_append_int32(reply, 0); gbinder_local_reply_append_bool(reply, success); return reply; } static GBinderLocalReply* test_servicemanager_hidl_handler( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestServiceManagerHidl* self = THIS(user_data); GBinderLocalReply* reply = NULL; g_assert(!flags); g_assert_cmpstr(gbinder_remote_request_interface(req), == ,MANAGER_IFACE); *status = -1; g_mutex_lock(&self->mutex); switch (code) { case GET_TRANSACTION: reply = test_servicemanager_hidl_get(self, req); break; case ADD_TRANSACTION: reply = test_servicemanager_hidl_add(self, req); break; case LIST_TRANSACTION: reply = test_servicemanager_hidl_list(self, req); break; case REGISTER_FOR_NOTIFICATIONS_TRANSACTION: reply = test_servicemanager_hidl_register_for_notifications(self, req); break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); return reply; } /*==========================================================================* * Interface *==========================================================================*/ TestServiceManagerHidl* test_servicemanager_hidl_new( GBinderIpc* ipc) { TestServiceManagerHidl* self = g_object_new(THIS_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); gbinder_local_object_init_base(obj, ipc, servicemanager_hidl_ifaces, test_servicemanager_hidl_handler, self); gbinder_ipc_register_local_object(ipc, obj); return self; } void test_servicemanager_hidl_free( TestServiceManagerHidl* self) { gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self)); } GBinderIpc* test_servicemanager_hidl_ipc( TestServiceManagerHidl* self) { return self ? self->parent.ipc : 0; } guint test_servicemanager_hidl_object_count( TestServiceManagerHidl* self) { guint count = 0; if (self) { g_mutex_lock(&self->mutex); count = g_hash_table_size(self->objects); g_mutex_unlock(&self->mutex); } return count; } GBinderRemoteObject* test_servicemanager_hidl_lookup( TestServiceManagerHidl* self, const char* name) { GBinderRemoteObject* object = NULL; if (self) { g_mutex_lock(&self->mutex); object = g_hash_table_lookup(self->objects, name); g_mutex_unlock(&self->mutex); } return object; } gboolean test_servicemanager_hidl_remove( TestServiceManagerHidl* self, const char* fqname) { gboolean removed = FALSE; if (self) { GBinderRemoteObject* obj; /* Lock */ g_mutex_lock(&self->mutex); obj = g_hash_table_lookup(self->objects, fqname); if (obj) { GHashTableIter it; gpointer key, value; g_hash_table_iter_init(&it, self->objects); while (g_hash_table_iter_next(&it, &key, &value)) { if (value == obj) { GDEBUG("Removed name '%s' => %p", (char*) key, value); g_hash_table_iter_remove(&it); removed = TRUE; } } } g_mutex_unlock(&self->mutex); /* Unlock */ } return removed; } /*==========================================================================* * Internals *==========================================================================*/ static GBINDER_LOCAL_TRANSACTION_SUPPORT test_servicemanager_hidl_can_handle_transaction( GBinderLocalObject* object, const char* iface, guint code) { /* * Handle all transactions on the looper thread to avoid deadlocks. * Unlike the real situation, binder simulation has one main thread * shared by both sides of the binder socket connection. */ return GBINDER_LOCAL_TRANSACTION_LOOPER; } static GBinderLocalReply* test_servicemanager_hidl_handle_looper_transaction( GBinderLocalObject* object, GBinderRemoteRequest* req, guint code, guint flags, int* status) { if (!g_strcmp0(gbinder_remote_request_interface(req), MANAGER_IFACE)) { return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)-> handle_transaction(object, req, code, flags, status); } else { return GBINDER_LOCAL_OBJECT_CLASS(PARENT_CLASS)-> handle_looper_transaction(object, req, code, flags, status); } } static void test_servicemanager_hidl_finalize( GObject* object) { TestServiceManagerHidl* self = THIS(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); g_ptr_array_free(self->watchers, TRUE); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } static void test_servicemanager_hidl_init( TestServiceManagerHidl* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); self->watchers = g_ptr_array_new_with_free_func((GDestroyNotify) gbinder_client_unref); } static void test_servicemanager_hidl_class_init( TestServiceManagerHidlClass* klass) { GObjectClass* object = G_OBJECT_CLASS(klass); GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass); object->finalize = test_servicemanager_hidl_finalize; local_object->can_handle_transaction = test_servicemanager_hidl_can_handle_transaction; local_object->handle_looper_transaction = test_servicemanager_hidl_handle_looper_transaction; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/common/test_servicemanager_hidl.h000066400000000000000000000047441526127616700234030ustar00rootroot00000000000000/* * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2021-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TEST_SERVICEMANAGER_HIDL_H #define TEST_SERVICEMANAGER_HIDL_H #include typedef struct test_servicemanager_hidl TestServiceManagerHidl; TestServiceManagerHidl* test_servicemanager_hidl_new( GBinderIpc* ipc); void test_servicemanager_hidl_free( TestServiceManagerHidl* sm); GBinderIpc* test_servicemanager_hidl_ipc( TestServiceManagerHidl* self); guint test_servicemanager_hidl_object_count( TestServiceManagerHidl* self); GBinderRemoteObject* test_servicemanager_hidl_lookup( TestServiceManagerHidl* self, const char* name); gboolean test_servicemanager_hidl_remove( TestServiceManagerHidl* self, const char* fqname); #endif /* TEST_SERVICEMANAGER_HIDL_H */ /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/coverage/000077500000000000000000000000001526127616700164725ustar00rootroot00000000000000libgbinder-1.1.54/unit/coverage/.gitignore000066400000000000000000000000161526127616700204570ustar00rootroot00000000000000report *.gcov libgbinder-1.1.54/unit/coverage/run000077500000000000000000000035651526127616700172350ustar00rootroot00000000000000#!/bin/bash # # This script requires lcov, dirname # TESTS="\ unit_bridge \ unit_buffer \ unit_client \ unit_config \ unit_driver \ unit_eventloop \ unit_fmq \ unit_ipc \ unit_local_object \ unit_local_reply \ unit_local_request \ unit_log \ unit_protocol \ unit_proxy_object \ unit_reader \ unit_remote_object \ unit_remote_reply \ unit_remote_request \ unit_servicemanager \ unit_servicemanager_aidl \ unit_servicemanager_aidl2 \ unit_servicemanager_aidl3 \ unit_servicemanager_aidl5 \ unit_servicemanager_aidl6 \ unit_servicemanager_hidl \ unit_servicename \ unit_servicepoll \ unit_writer" function err() { echo "*** ERROR!" $1 exit 1 } # Check the required tools which lcov >> /dev/null || err "Please install lcov" which dirname >> /dev/null || err "Please install dirname" # LCOV 1.10 has branch coverage disabled per default # Previous versions didn't have the --rc option if [ ! -z "$(lcov --help | grep ' --rc ')" ] ; then LCOV_OPT="--rc lcov_branch_coverage=1" GENHTML_OPT="--branch-coverage" fi # If specific tests are provided on the command line, only run those if [ $# -gt 0 ] ; then TESTS="$@" fi pushd `dirname $0` > /dev/null COV_DIR="$PWD" pushd .. > /dev/null TEST_DIR="$PWD" pushd .. > /dev/null TOP_DIR="$PWD" popd > /dev/null popd > /dev/null popd > /dev/null make -C "$TOP_DIR" clean for t in $TESTS ; do pushd "$TEST_DIR/$t" make -C "$TEST_DIR/$t" clean coverage || exit 1 build/coverage/$t || exit 1 popd done # Sometimes you need this, sometimes that :S BASE_DIR="$TOP_DIR" #BASE_DIR="$TOP_DIR/src" FULL_COV="$COV_DIR/full.gcov" LIB_COV="$COV_DIR/lib.gcov" rm -f "$FULL_COV" "$LIB_COV" lcov $LCOV_OPT -c -d "$TOP_DIR/build/coverage" -b "$BASE_DIR" -o "$FULL_COV" || exit 1 lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/*" -o "$LIB_COV" || exit 1 genhtml $GENHTML_OPT "$LIB_COV" -t "libgbinder" --output-directory "$COV_DIR/report" || exit 1 libgbinder-1.1.54/unit/unit_bridge/000077500000000000000000000000001526127616700171725ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_bridge/Makefile000066400000000000000000000002201526127616700206240ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_bridge COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c include ../common/Makefile libgbinder-1.1.54/unit/unit_bridge/unit_bridge.c000066400000000000000000000401351526127616700216340ustar00rootroot00000000000000/* * Copyright (C) 2021-2023 Slava Monich * Copyright (C) 2021-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "test_servicemanager_hidl.h" #include "gbinder_ipc.h" #include "gbinder_client.h" #include "gbinder_config.h" #include "gbinder_driver.h" #include "gbinder_bridge.h" #include "gbinder_reader.h" #include "gbinder_local_reply.h" #include "gbinder_local_request.h" #include "gbinder_proxy_object.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_reply.h" #include "gbinder_remote_request.h" #include "gbinder_servicemanager_p.h" #include static TestOpt test_opt; #define SRC_DEV "/dev/srcbinder" #define DEST_DEV "/dev/dstbinder" #define TEST_IFACE "gbinder@1.0::ITest" #define TX_CODE GBINDER_FIRST_CALL_TRANSACTION #define TX_PARAM 0x11111111 #define TX_RESULT 0x22222222 static const char TMP_DIR_TEMPLATE[] = "gbinder-test-bridge-XXXXXX"; static const char* TEST_IFACES[] = { TEST_IFACE, NULL }; static const char DEFAULT_CONFIG_DATA[] = "[Protocol]\n" "Default = hidl\n" "[ServiceManager]\n" "Default = hidl\n"; /*==========================================================================* * Common *==========================================================================*/ static TestServiceManagerHidl* test_servicemanager_impl_new( const char* dev) { GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); TestServiceManagerHidl* sm = test_servicemanager_hidl_new(ipc); test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm), GBINDER_SERVICEMANAGER_HANDLE); gbinder_ipc_unref(ipc); return sm; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { static const char* ifaces[] = { "foo", "bar", NULL}; g_assert(!gbinder_bridge_new2(NULL, NULL, NULL, NULL, NULL)); g_assert(!gbinder_bridge_new3(NULL, NULL, NULL, NULL)); g_assert(!gbinder_bridge_new(NULL, NULL, NULL, NULL)); g_assert(!gbinder_bridge_new("foo", NULL, NULL, NULL)); g_assert(!gbinder_bridge_new("foo", ifaces, NULL, NULL)); gbinder_bridge_free(NULL); } /*==========================================================================* * basic *==========================================================================*/ typedef struct test_basic { GMainLoop* loop; TestServiceManagerHidl* src_impl; int src_notify_count; gboolean dest_name_added; } TestBasic; static GBinderLocalReply* test_basic_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* count = user_data; GBinderReader reader; gint32 param = 0; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); /* Make sure that parameter got delivered intact */ gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_read_int32(&reader, ¶m)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(param, == ,TX_PARAM); *status = GBINDER_STATUS_OK; (*count)++; GDEBUG("Got a request, replying"); return gbinder_local_reply_append_int32 (gbinder_local_object_new_reply(obj), TX_RESULT); } static void test_basic_add_cb( GBinderServiceManager* sm, int status, void* user_data) { TestBasic* test = user_data; GDEBUG("Name added"); g_assert(status == GBINDER_STATUS_OK); g_assert(!test->dest_name_added); test->dest_name_added = TRUE; if (test->src_notify_count) { g_main_loop_quit(test->loop); } } static void test_basic_notify_cb( GBinderServiceManager* sm, const char* name, void* user_data) { TestBasic* test = user_data; g_assert(name); GDEBUG("'%s' is registered", name); g_assert_cmpint(test->src_notify_count, == ,0); test->src_notify_count++; /* Exit the loop after both things happen */ if (test->dest_name_added) { g_main_loop_quit(test->loop); } } static void test_basic_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* loop) { GBinderReader reader; gint32 result = 0; g_assert(reply); GDEBUG("Reply received"); /* Make sure that result got delivered intact */ gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(result, == ,TX_RESULT); /* Exit the loop */ g_main_loop_quit((GMainLoop*)loop); } static void test_basic_death( GBinderRemoteObject* obj, void* loop) { GDEBUG("Source object died"); /* Exit the loop */ g_main_loop_quit((GMainLoop*)loop); } static void test_basic_run( void) { TestBasic test; TestServiceManagerHidl* dest_impl; GBinderServiceManager* src; GBinderServiceManager* dest; GBinderIpc* src_ipc; GBinderIpc* dest_ipc; GBinderBridge* bridge; GBinderLocalObject* obj; GBinderRemoteObject* src_obj; GBinderLocalRequest* req; GBinderClient* src_client; const char* name = "test"; const char* fqname = TEST_IFACE "/test"; int src_fd, dest_fd, n = 0; gulong id; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); /* obj (DEST) <=> bridge <=> (SRC) mirror */ src_ipc = gbinder_ipc_new(SRC_DEV, NULL); dest_ipc = gbinder_ipc_new(DEST_DEV, NULL); test.src_impl = test_servicemanager_impl_new(SRC_DEV); dest_impl = test_servicemanager_impl_new(DEST_DEV); src_fd = gbinder_driver_fd(src_ipc->driver); dest_fd = gbinder_driver_fd(dest_ipc->driver); obj = gbinder_local_object_new(dest_ipc, TEST_IFACES, test_basic_cb, &n); /* Set up binder simulator */ src = gbinder_servicemanager_new(SRC_DEV); dest = gbinder_servicemanager_new(DEST_DEV); /* Both src and dest are required */ g_assert(!gbinder_bridge_new(name, TEST_IFACES, src, NULL)); bridge = gbinder_bridge_new2(NULL, name, TEST_IFACES, src, dest); /* Start watching the name */ id = gbinder_servicemanager_add_registration_handler(src, fqname, test_basic_notify_cb, &test); g_assert(id); /* Register the object and wait for completion */ GDEBUG("Registering object '%s' => %p", name, obj); g_assert(gbinder_servicemanager_add_service(dest, name, obj, test_basic_add_cb, &test)); /* This loop quits after the name is added and notification is received */ test_run(&test_opt, test.loop); GDEBUG("Bridge object has been registered on source"); g_assert_cmpint(test.src_notify_count, == ,1); gbinder_servicemanager_remove_handler(src, id); /* Get a remote reference to the object created by the bridge */ src_obj = gbinder_servicemanager_get_service_sync(src, fqname, NULL); g_assert(!src_obj->dead); /* Make a call */ GDEBUG("Submitting a call"); /* src_client will hold a reference to src_obj */ src_client = gbinder_client_new(src_obj, TEST_IFACE); req = gbinder_client_new_request(src_client); gbinder_local_request_append_int32(req, TX_PARAM); g_assert(gbinder_client_transact(src_client, TX_CODE, 0, req, test_basic_reply, NULL, test.loop)); gbinder_local_request_unref(req); /* Wait for completion */ test_run(&test_opt, test.loop); /* Kill the objects and wait for one of them to die */ g_assert(!src_obj->dead); id = gbinder_remote_object_add_death_handler(src_obj, test_basic_death, test.loop); g_assert(test_servicemanager_hidl_remove(dest_impl, fqname)); GDEBUG("Killing destination objects"); /* * Need these BR_DEAD_BINDER because both servicemanagers and the * bridge live inside the same process and reference the same objects. * BR_DEAD_BINDER forces the bridge (proxy) to drop its reference. */ test_binder_br_dead_binder_obj(dest_fd, obj); test_binder_br_dead_binder(src_fd, ANY_THREAD, src_obj->handle); /* Wait for the auto-created object to die */ test_run(&test_opt, test.loop); g_assert(src_obj->dead); gbinder_remote_object_remove_handler(src_obj, id); GDEBUG("Done"); gbinder_local_object_drop(obj); gbinder_bridge_free(bridge); test_servicemanager_hidl_free(test.src_impl); test_servicemanager_hidl_free(dest_impl); gbinder_servicemanager_unref(src); gbinder_servicemanager_unref(dest); gbinder_client_unref(src_client); gbinder_ipc_unref(src_ipc); gbinder_ipc_unref(dest_ipc); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_basic( void) { test_run_in_context(&test_opt, test_basic_run); } /*==========================================================================* * direct_name *==========================================================================*/ static void test_direct_name_run( void) { TestBasic test; TestServiceManagerHidl* dest_impl; GBinderServiceManager* src; GBinderServiceManager* dest; GBinderIpc* src_ipc; GBinderIpc* dest_ipc; GBinderBridge* bridge; GBinderLocalObject* obj; GBinderRemoteObject* src_obj; GBinderLocalRequest* req; GBinderClient* src_client; const char* name = TEST_IFACE "/test"; int src_fd, dest_fd, n = 0; gulong id; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); /* obj (DEST) <=> bridge <=> (SRC) mirror */ src_ipc = gbinder_ipc_new(SRC_DEV, NULL); dest_ipc = gbinder_ipc_new(DEST_DEV, NULL); test.src_impl = test_servicemanager_impl_new(SRC_DEV); dest_impl = test_servicemanager_impl_new(DEST_DEV); src_fd = gbinder_driver_fd(src_ipc->driver); dest_fd = gbinder_driver_fd(dest_ipc->driver); obj = gbinder_local_object_new(dest_ipc, TEST_IFACES, test_basic_cb, &n); /* Set up binder simulator */ src = gbinder_servicemanager_new(SRC_DEV); dest = gbinder_servicemanager_new(DEST_DEV); /* Direct service name mode */ bridge = gbinder_bridge_new(name, NULL, src, dest); g_assert(bridge); /* Start watching the name */ id = gbinder_servicemanager_add_registration_handler(src, name, test_basic_notify_cb, &test); g_assert(id); /* Register the object and wait for completion */ GDEBUG("Registering object '%s' => %p", name, obj); g_assert(gbinder_servicemanager_add_service(dest, name, obj, test_basic_add_cb, &test)); /* This loop quits after the name is added and notification is received */ test_run(&test_opt, test.loop); GDEBUG("Bridge object has been registered on source"); g_assert_cmpint(test.src_notify_count, == ,1); gbinder_servicemanager_remove_handler(src, id); /* Get a remote reference to the object created by the bridge */ src_obj = gbinder_servicemanager_get_service_sync(src, name, NULL); g_assert(!src_obj->dead); /* Make a call */ GDEBUG("Submitting a call"); /* src_client will hold a reference to src_obj */ src_client = gbinder_client_new(src_obj, TEST_IFACE); req = gbinder_client_new_request(src_client); gbinder_local_request_append_int32(req, TX_PARAM); g_assert(gbinder_client_transact(src_client, TX_CODE, 0, req, test_basic_reply, NULL, test.loop)); gbinder_local_request_unref(req); /* Wait for completion */ test_run(&test_opt, test.loop); /* Kill the objects and wait for one of them to die */ g_assert(!src_obj->dead); id = gbinder_remote_object_add_death_handler(src_obj, test_basic_death, test.loop); g_assert(test_servicemanager_hidl_remove(dest_impl, name)); GDEBUG("Killing destination objects"); test_binder_br_dead_binder_obj(dest_fd, obj); test_binder_br_dead_binder(src_fd, ANY_THREAD, src_obj->handle); /* Wait for the auto-created object to die */ test_run(&test_opt, test.loop); g_assert(src_obj->dead); gbinder_remote_object_remove_handler(src_obj, id); GDEBUG("Done"); gbinder_local_object_drop(obj); gbinder_bridge_free(bridge); test_servicemanager_hidl_free(test.src_impl); test_servicemanager_hidl_free(dest_impl); gbinder_servicemanager_unref(src); gbinder_servicemanager_unref(dest); gbinder_client_unref(src_client); gbinder_ipc_unref(src_ipc); gbinder_ipc_unref(dest_ipc); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_direct_name( void) { test_run_in_context(&test_opt, test_direct_name_run); } /*==========================================================================* * incompatible *==========================================================================*/ static void test_incompatible( void) { const char* name = "test"; const char* fqname = TEST_IFACE "/test"; GBinderServiceManager* aidl = gbinder_defaultservicemanager_new(SRC_DEV); GBinderServiceManager* hidl = gbinder_hwservicemanager_new(DEST_DEV); g_assert(aidl); g_assert(hidl); g_assert(!gbinder_bridge_new(name, TEST_IFACES, aidl, hidl)); g_assert(!gbinder_bridge_new(name, TEST_IFACES, hidl, aidl)); g_assert(!gbinder_bridge_new3(NULL, fqname, aidl, hidl)); g_assert(!gbinder_bridge_new3(NULL, fqname, hidl, aidl)); gbinder_servicemanager_unref(aidl); gbinder_servicemanager_unref(hidl); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/bridge/" t int main(int argc, char* argv[]) { TestConfig test_config; char* config_file; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("direct_name"), test_direct_name); g_test_add_func(TEST_("incompatible"), test_incompatible); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); config_file = g_build_filename(test_config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(config_file, DEFAULT_CONFIG_DATA, -1, NULL)); GDEBUG("Config file %s", config_file); gbinder_config_file = config_file; result = g_test_run(); remove(config_file); g_free(config_file); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_buffer/000077500000000000000000000000001526127616700172075ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_buffer/Makefile000066400000000000000000000001161526127616700206450ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_buffer include ../common/Makefile libgbinder-1.1.54/unit/unit_buffer/unit_buffer.c000066400000000000000000000134331526127616700216670ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * Copyright (C) 2018-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_driver.h" #include "gbinder_buffer_p.h" static TestOpt test_opt; /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(NULL, NULL, 0, NULL); GBinderBuffer* buf2; gsize size = 1; gbinder_buffer_free(buf); /* No need to reference the driver if there's no data */ buf = gbinder_buffer_new(driver, NULL, 0, NULL); g_assert(!gbinder_buffer_driver(buf)); gbinder_buffer_free(buf); buf = gbinder_buffer_new_with_parent(NULL, NULL, 0); buf2 = gbinder_buffer_new_with_parent(buf, NULL, 0); g_assert(!gbinder_buffer_objects(buf)); g_assert(!gbinder_buffer_objects(buf2)); g_assert(!gbinder_buffer_driver(buf)); g_assert(!gbinder_buffer_driver(buf2)); gbinder_buffer_free(buf); gbinder_buffer_free(buf2); gbinder_buffer_free(NULL); gbinder_buffer_contents_list_free(NULL); g_assert(!gbinder_buffer_driver(NULL)); g_assert(!gbinder_buffer_objects(NULL)); g_assert(!gbinder_buffer_io(NULL)); g_assert(!gbinder_buffer_data(NULL, NULL)); g_assert(!gbinder_buffer_data(NULL, &size)); g_assert(!gbinder_buffer_contents(NULL)); g_assert(!gbinder_buffer_contents_list_add(NULL, NULL)); g_assert(!gbinder_buffer_contents_list_dup(NULL)); g_assert(!size); gbinder_driver_unref(driver); } /*==========================================================================* * list *==========================================================================*/ static void test_list( void) { static const guint8 data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; void* ptr = g_memdup(data, sizeof(data)); GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(driver, ptr, sizeof(data), NULL); GBinderBufferContents* contents = gbinder_buffer_contents(buf); GBinderBufferContentsList* list = gbinder_buffer_contents_list_add (NULL, contents); GBinderBufferContentsList* list2 = gbinder_buffer_contents_list_dup(list); g_assert(contents); g_assert(list); g_assert(list2); gbinder_buffer_free(buf); gbinder_buffer_contents_list_free(list); gbinder_buffer_contents_list_free(list2); gbinder_driver_unref(driver); } /*==========================================================================* * parent *==========================================================================*/ static void test_parent( void) { static const guint8 data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; void* ptr = g_memdup(data, sizeof(data)); gsize size = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* parent = gbinder_buffer_new(driver, ptr, sizeof(data), NULL); GBinderBuffer* buf = gbinder_buffer_new_with_parent (parent, ptr, sizeof(data)); g_assert(gbinder_buffer_driver(buf) == driver); g_assert(gbinder_buffer_io(buf)); g_assert(gbinder_buffer_io(buf) == gbinder_driver_io(driver)); g_assert(gbinder_buffer_contents(buf)); g_assert(gbinder_buffer_data(buf, NULL) == ptr); g_assert(gbinder_buffer_data(buf, &size) == ptr); g_assert(size == sizeof(data)); gbinder_buffer_free(buf); gbinder_buffer_free(parent); gbinder_driver_unref(driver); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/buffer/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("list"), test_list); g_test_add_func(TEST_("parent"), test_parent); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_client/000077500000000000000000000000001526127616700172145ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_client/Makefile000066400000000000000000000001161526127616700206520ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_client include ../common/Makefile libgbinder-1.1.54/unit/unit_client/unit_client.c000066400000000000000000000360501526127616700217010ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_client_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_local_reply_p.h" #include "gbinder_local_request.h" #include "gbinder_object_registry.h" #include "gbinder_output_data.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_reply.h" #include "gbinder_writer.h" #include #include static TestOpt test_opt; static GBinderClient* test_client_new( guint h, const char* iface) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE); GBinderClient* client = gbinder_client_new(obj, iface); g_assert(client); gbinder_remote_object_unref(obj); gbinder_ipc_unref(ipc); return client; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { g_assert(!gbinder_client_new(NULL, NULL)); g_assert(!gbinder_client_new2(NULL, NULL, 0)); g_assert(!gbinder_client_ref(NULL)); g_assert(!gbinder_client_interface(NULL)); g_assert(!gbinder_client_interface2(NULL, 0)); g_assert(!gbinder_client_rpc_header(NULL, 0)); gbinder_client_unref(NULL); g_assert(!gbinder_client_new_request(NULL)); g_assert(!gbinder_client_new_request2(NULL, 0)); g_assert(!gbinder_client_transact_sync_reply(NULL, 0, NULL, NULL)); g_assert(gbinder_client_transact_sync_oneway(NULL, 0, NULL) == (-EINVAL)); g_assert(!gbinder_client_transact(NULL, 0, 0, NULL, NULL, NULL, NULL)); gbinder_client_cancel(NULL, 0); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE); const char* iface = "foo"; GBinderClient* client = gbinder_client_new(obj, iface); g_assert(client); g_assert(gbinder_client_ref(client) == client); g_assert_cmpstr(gbinder_client_interface(client), == ,iface); gbinder_client_unref(client); gbinder_client_cancel(client, 0); /* does nothing */ gbinder_client_unref(client); gbinder_remote_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * interfaces *==========================================================================*/ static void test_interfaces( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0, TRUE); static const GBinderClientIfaceInfo ifaces[] = { {"33", 33 }, { "11", 11 }, { "22", 22 } }; GBinderClient* client = gbinder_client_new2(obj, ifaces, G_N_ELEMENTS(ifaces)); GBinderWriter writer; GBinderLocalRequest* req; GBytes* rpc_header; gsize len; const void* hdr; g_assert(client); g_assert_cmpstr(gbinder_client_interface(client), == ,"11"); g_assert_cmpstr(gbinder_client_interface2(client, 11), == ,"11"); g_assert_cmpstr(gbinder_client_interface2(client, 22), == ,"22"); g_assert_cmpstr(gbinder_client_interface2(client, 33), == ,"33"); g_assert(!gbinder_client_interface2(client, 34)); g_assert(!gbinder_client_rpc_header(client, 34)); g_assert(!gbinder_client_new_request2(client, 34)); /* Those fail to allocate default request for out-of-range codes: */ g_assert(!gbinder_client_transact_sync_reply(client, 34, NULL, NULL)); g_assert_cmpint(gbinder_client_transact_sync_oneway(client, 34, NULL), == ,-EINVAL); g_assert(!gbinder_client_transact(client, 34, 0, NULL, NULL, NULL, NULL)); /* Check the RPC header */ rpc_header = gbinder_client_rpc_header(client, 33); req = gbinder_client_new_request2(client, 33); g_assert(rpc_header); g_assert(req); gbinder_local_request_init_writer(req, &writer); hdr = gbinder_writer_get_data(&writer, &len); g_assert(hdr); g_assert_cmpuint(len, == ,g_bytes_get_size(rpc_header)); g_assert(!memcmp(hdr, g_bytes_get_data(rpc_header, NULL), len)); gbinder_local_request_unref(req); gbinder_client_unref(client); /* Client with no interface info */ client = gbinder_client_new2(obj, NULL, 0); g_assert(client); g_assert(!gbinder_client_interface(client)); g_assert(!gbinder_client_interface2(client, 1)); gbinder_client_unref(client); gbinder_remote_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * no_header *==========================================================================*/ static void test_no_header( void) { GBinderClient* client = test_client_new(0, NULL); int fd = gbinder_driver_fd(gbinder_client_ipc(client)->driver); test_binder_ignore_dead_object(fd); test_binder_br_transaction_complete(fd, THIS_THREAD); g_assert(gbinder_client_transact_sync_oneway(client, 0, NULL) == GBINDER_STATUS_OK); gbinder_client_unref(client); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * dead *==========================================================================*/ static void test_dead_done( GBinderRemoteObject* obj, void* user_data) { GVERBOSE_(""); test_quit_later((GMainLoop*)user_data); } static void test_dead( void) { const guint handle = 1; GBinderClient* client = test_client_new(handle, "foo"); GBinderRemoteObject* obj = client->remote; GMainLoop* loop = g_main_loop_new(NULL, FALSE); const int fd = gbinder_driver_fd(gbinder_client_ipc(client)->driver); gbinder_remote_object_add_death_handler(obj, test_dead_done, loop); test_binder_br_dead_binder(fd, ANY_THREAD, handle); test_run(&test_opt, loop); g_assert(gbinder_remote_object_is_dead(obj)); g_assert(!gbinder_client_transact_sync_reply(client, 0, NULL, NULL)); g_assert(gbinder_client_transact_sync_oneway(client, 0, NULL) == -ESTALE); g_assert(!gbinder_client_transact(client, 0, 0, NULL, NULL, NULL, NULL)); gbinder_client_unref(client); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * sync_oneway *==========================================================================*/ static void test_sync_oneway( void) { GBinderClient* client = test_client_new(0, "foo"); GBinderLocalRequest* req = gbinder_client_new_request(client); int fd = gbinder_driver_fd(gbinder_client_ipc(client)->driver); g_assert(req); test_binder_br_transaction_complete(fd, THIS_THREAD); g_assert(gbinder_client_transact_sync_oneway(client, 0, req) == GBINDER_STATUS_OK); gbinder_local_request_unref(req); /* Same but using the internal (empty) request */ test_binder_br_transaction_complete(fd, THIS_THREAD); g_assert(gbinder_client_transact_sync_oneway(client, 0, NULL) == GBINDER_STATUS_OK); gbinder_client_unref(client); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * sync_reply *==========================================================================*/ static void test_sync_reply_tx( GBinderClient* client, GBinderLocalRequest* req) { GBinderDriver* driver = gbinder_client_ipc(client)->driver; int fd = gbinder_driver_fd(driver); const GBinderIo* io = gbinder_driver_io(driver); const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver); GBinderLocalReply* reply = gbinder_local_reply_new(io, protocol); GBinderRemoteReply* tx_reply; GBinderOutputData* data; const guint32 handle = 0; const guint32 code = 1; const char* result_in = "foo"; char* result_out; int status = INT_MAX; g_assert(gbinder_local_reply_append_string16(reply, result_in)); data = gbinder_local_reply_data(reply); g_assert(data); test_binder_ignore_dead_object(fd); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, handle, code, data->bytes); tx_reply = gbinder_client_transact_sync_reply(client, 0, req, &status); g_assert(tx_reply); g_assert(status == GBINDER_STATUS_OK); result_out = gbinder_remote_reply_read_string16(tx_reply); g_assert(!g_strcmp0(result_out, result_in)); g_free(result_out); gbinder_remote_reply_unref(tx_reply); gbinder_local_reply_unref(reply); } static void test_sync_reply( void) { GBinderClient* client = test_client_new(0, "foo"); GBinderLocalRequest* req = gbinder_client_new_request(client); test_sync_reply_tx(client, req); gbinder_local_request_unref(req); /* Same but using the internal (empty) request */ test_sync_reply_tx(client, NULL); gbinder_client_unref(client); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * reply *==========================================================================*/ #define TEST_INTERFACE "foo" #define TEST_REQ_PARAM_STR "bar" static void test_reply_destroy( void* user_data) { test_quit_later((GMainLoop*)user_data); } static void test_reply_ok_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { char* result; GVERBOSE_("%d", status); g_assert(status == GBINDER_STATUS_OK); g_assert(reply); result = gbinder_remote_reply_read_string16(reply); g_assert(!g_strcmp0(result, TEST_REQ_PARAM_STR)); g_free(result); } static void test_reply_ok_quit( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { test_reply_ok_reply(client, reply, status, user_data); test_reply_destroy(user_data); } static void test_reply_tx( GBinderClient* client, GBinderLocalRequest* req, GBinderClientReplyFunc done, GDestroyNotify destroy) { GBinderDriver* driver = gbinder_client_ipc(client)->driver; int fd = gbinder_driver_fd(driver); const GBinderIo* io = gbinder_driver_io(driver); const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver); GBinderLocalReply* reply = gbinder_local_reply_new(io, protocol); GBinderOutputData* data; const guint32 handle = 0; const guint32 code = 1; GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR)); data = gbinder_local_reply_data(reply); g_assert(data); test_binder_ignore_dead_object(fd); test_binder_br_noop(fd, TX_THREAD); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply(fd, TX_THREAD, handle, code, data->bytes); id = gbinder_client_transact(client, 0, 0, req, done, destroy, loop); g_assert(id); test_run(&test_opt, loop); gbinder_local_reply_unref(reply); g_main_loop_unref(loop); } static void test_reply( GBinderClientReplyFunc done, GDestroyNotify destroy) { GBinderClient* client = test_client_new(0, TEST_INTERFACE); GBinderLocalRequest* req = gbinder_client_new_request2(client, 0); g_assert(req); test_reply_tx(client, req, done, destroy); gbinder_local_request_unref(req); /* Same but using the internal (empty) request */ test_reply_tx(client, NULL, done, destroy); gbinder_client_unref(client); test_binder_exit_wait(&test_opt, NULL); } static void test_reply_ok1( void) { test_reply(test_reply_ok_reply, test_reply_destroy); } static void test_reply_ok2( void) { test_reply(NULL, test_reply_destroy); } static void test_reply_ok3( void) { test_reply(test_reply_ok_quit, NULL); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/client/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("interfaces"), test_interfaces); g_test_add_func(TEST_("dead"), test_dead); g_test_add_func(TEST_("no_header"), test_no_header); g_test_add_func(TEST_("sync_oneway"), test_sync_oneway); g_test_add_func(TEST_("sync_reply"), test_sync_reply); g_test_add_func(TEST_("reply/ok1"), test_reply_ok1); g_test_add_func(TEST_("reply/ok2"), test_reply_ok2); g_test_add_func(TEST_("reply/ok3"), test_reply_ok3); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_config/000077500000000000000000000000001526127616700172035ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_config/Makefile000066400000000000000000000001161526127616700206410ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_config include ../common/Makefile libgbinder-1.1.54/unit/unit_config/unit_config.c000066400000000000000000000434021526127616700216560ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2020-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_config.h" #include #include #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-config-XXXXXX"; static const char* test_value( GKeyFile* keyfile, const char* group, const char* key, GString* buf) { char* value = g_key_file_get_value(keyfile, group, key, NULL); g_string_set_size(buf, 0); if (value) { g_string_append(buf, value); g_free(value); return buf->str; } else { return NULL; } } static gboolean test_keyfiles_equal( GKeyFile* keyfile1, GKeyFile* keyfile2) { gboolean equal = FALSE; gsize ngroups; char** groups = g_key_file_get_groups(keyfile1, &ngroups); char** groups2 = g_key_file_get_groups(keyfile2, NULL); gutil_strv_sort(groups, TRUE); gutil_strv_sort(groups2, TRUE); if (gutil_strv_equal(groups, groups2)) { gsize i; equal = TRUE; for (i = 0; i < ngroups && equal; i++) { const char* group = groups[i]; gsize nkeys; char** keys = g_key_file_get_keys(keyfile1, group, &nkeys, NULL); char** keys2 = g_key_file_get_keys(keyfile2, group, &nkeys, NULL); equal = FALSE; gutil_strv_sort(keys, TRUE); gutil_strv_sort(keys2, TRUE); if (gutil_strv_equal(keys, keys2)) { gsize k; equal = TRUE; for (k = 0; k < nkeys && equal; k++) { const char* key = keys[k]; char* v1 = g_key_file_get_value(keyfile1, group, key, NULL); char* v2 = g_key_file_get_value(keyfile2, group, key, NULL); if (g_strcmp0(v1, v2)) { equal = FALSE; GDEBUG("Values for %s/%s don't match ('%s' vs '%s')", group, key, v1, v2); } g_free(v1); g_free(v2); } } else { GDEBUG("Keys for %s don't match", group); } g_strfreev(keys); g_strfreev(keys2); } } else { GDEBUG("Groups don't match"); } g_strfreev(groups); g_strfreev(groups2); if (!equal) { char* data1 = g_key_file_to_data(keyfile1, NULL, NULL); char* data2 = g_key_file_to_data(keyfile2, NULL, NULL); GDEBUG("This:"); GDEBUG("%s", data1); GDEBUG("Doesn't match this:"); GDEBUG("%s", data2); g_free(data1); g_free(data2); } return equal; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { const char* default_name = gbinder_config_file; /* Reset the state */ gbinder_config_exit(); gbinder_config_file = NULL; gbinder_config_dir = NULL; g_assert(!gbinder_config_get()); /* Reset the state again */ gbinder_config_file = default_name; } /*==========================================================================* * non_exist *==========================================================================*/ static void test_non_exit( void) { const char* default_name = gbinder_config_file; char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL); char* file = g_build_filename(dir, "test.conf", NULL); /* Reset the state */ gbinder_config_exit(); gbinder_config_file = file; g_assert(!gbinder_config_get()); /* Reset the state again */ gbinder_config_file = default_name; g_free(file); remove(dir); g_free(dir); } /*==========================================================================* * bad_config *==========================================================================*/ static void test_bad_config( void) { const char* default_name = gbinder_config_file; char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL); char* file = g_build_filename(dir, "test.conf", NULL); static const char garbage[] = "foo"; /* Reset the state */ gbinder_config_exit(); /* Try to load the garbage */ g_assert(g_file_set_contents(file, garbage, -1, NULL)); gbinder_config_file = file; g_assert(!gbinder_config_get()); /* Reset the state again */ gbinder_config_file = default_name; remove(file); g_free(file); remove(dir); g_free(dir); } /*==========================================================================* * dirs *==========================================================================*/ static void test_dirs( void) { GKeyFile* k; GString* b = g_string_new(NULL); const char* default_file = gbinder_config_file; const char* default_dir = gbinder_config_dir; char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL); char* subdir = g_build_filename(dir, "d", NULL); char* notafile = g_build_filename(subdir, "dir.conf", NULL); char* file = g_build_filename(dir, "test.conf", NULL); char* file1 = g_build_filename(subdir, "a.conf", NULL); char* file2 = g_build_filename(subdir, "b.conf", NULL); char* file3 = g_build_filename(subdir, "c.conf", NULL); char* file4 = g_build_filename(subdir, "d.conf", NULL); char* random_file = g_build_filename(subdir, "foo", NULL); static const char garbage[] = "foo"; static const char config[] = "[Protocol]\n" "/dev/binder = aidl\n" "/dev/hbinder = hidl\n"; static const char config1[] = "[Protocol]\n" "/dev/hwbinder = hidl\n" "[ServiceManager]\n" "/dev/binder = aidl\n"; static const char config2[] = "[Protocol]\n" "/dev/binder = aidl2\n" "[ServiceManager]\n" "/dev/binder = aidl2\n"; static const char config3[] = "[Protocol]\n" "/dev/binder3 = aidl3\n" "[ServiceManager]\n" "/dev/binder3 = aidl3\n"; static const char config4[] = "[Protocol]\n" "/dev/binder4 = aidl3\n" "[ServiceManager]\n" "/dev/binder4 = aidl4\n"; g_assert_cmpint(mkdir(subdir, 0700), == ,0); g_assert_cmpint(mkdir(notafile, 0700), == ,0); g_assert(g_file_set_contents(file, config, -1, NULL)); g_assert(g_file_set_contents(file1, config1, -1, NULL)); g_assert(g_file_set_contents(file2, config2, -1, NULL)); g_assert(g_file_set_contents(file3, config3, -1, NULL)); g_assert(g_file_set_contents(file4, config4, -1, NULL)); g_assert(g_file_set_contents(random_file, garbage, -1, NULL)); /* Reset the state */ gbinder_config_exit(); gbinder_config_file = file; gbinder_config_dir = subdir; /* Load the config */ k = gbinder_config_get(); g_assert(k); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder4",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/hbinder",b), == ,"hidl"); g_assert_cmpstr(test_value(k,"Protocol","/dev/hwbinder",b), == ,"hidl"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder4",b),==,"aidl4"); /* Remove the default file and try again */ gbinder_config_exit(); g_assert_cmpint(remove(file), == ,0); k = gbinder_config_get(); g_assert(k); g_assert(!test_value(k,"Protocol","/dev/hbinder",b)); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder4",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/hwbinder",b), == ,"hidl"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder4",b),==,"aidl4"); /* Damage one of the files and try again */ gbinder_config_exit(); g_assert(g_file_set_contents(file1, garbage, -1, NULL)); k = gbinder_config_get(); g_assert(k); g_assert(!test_value(k,"Protocol","/dev/hbinder",b)); g_assert(!test_value(k,"Protocol","/dev/hwbinder",b)); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder4",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder4",b),==,"aidl4"); /* Disallow access to one of the files and try again */ gbinder_config_exit(); g_assert_cmpint(chmod(file1, 0), == ,0); k = gbinder_config_get(); g_assert(k); g_assert(!test_value(k,"Protocol","/dev/hbinder",b)); g_assert(!test_value(k,"Protocol","/dev/hwbinder",b)); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder",b), == ,"aidl2"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder3",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder4",b), == ,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder3",b),==,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder4",b),==,"aidl4"); /* Leave only one file (file4) in the subdirectory */ gbinder_config_exit(); g_assert_cmpint(remove(file1), == ,0); g_assert_cmpint(remove(file2), == ,0); g_assert_cmpint(remove(file3), == ,0); k = gbinder_config_get(); g_assert(k); g_assert(!test_value(k,"Protocol","/dev/hbinder",b)); g_assert(!test_value(k,"Protocol","/dev/hwbinder",b)); g_assert(!test_value(k,"Protocol","/dev/binder3",b)); g_assert_cmpstr(test_value(k,"Protocol","/dev/binder4",b),==,"aidl3"); g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder4",b),==,"aidl4"); /* Delete the remaining file and try again */ gbinder_config_exit(); g_assert_cmpint(remove(file4), == ,0); g_assert(!gbinder_config_get()); /* Undo all the damage */ gbinder_config_exit(); gbinder_config_file = default_file; gbinder_config_dir = default_dir; remove(random_file); g_free(file); g_free(file1); g_free(file2); g_free(file3); g_free(file4); g_free(random_file); remove(notafile); remove(subdir); remove(dir); g_free(notafile); g_free(subdir); g_free(dir); g_string_free(b, TRUE); } /*==========================================================================* * autorelease *==========================================================================*/ static void test_autorelease( void) { const char* default_file = gbinder_config_file; char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL); char* file = g_build_filename(dir, "test.conf", NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GKeyFile* keyfile; static const char config[] = "[Protocol]"; gbinder_config_exit(); /* Reset the state */ /* Load the file */ g_assert(g_file_set_contents(file, config, -1, NULL)); gbinder_config_file = file; keyfile = gbinder_config_get(); g_assert(keyfile); /* Second call returns the same pointer */ g_assert(keyfile == gbinder_config_get()); test_quit_later_n(loop, 2); test_run(&test_opt, loop); g_main_loop_unref(loop); /* Reset the state again */ gbinder_config_exit(); gbinder_config_file = default_file; remove(file); g_free(file); remove(dir); g_free(dir); } /*==========================================================================* * Presets *==========================================================================*/ typedef struct test_presets_data { const char* name; const char* in; const char* out; } TestPresetsData; static void test_presets( gconstpointer test_data) { const TestPresetsData* test = test_data; const char* default_file = gbinder_config_file; char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL); char* file = g_build_filename(dir, "test.conf", NULL); GKeyFile* expected = g_key_file_new(); GKeyFile* keyfile; /* Reset the state */ gbinder_config_exit(); /* Load the file */ if (test->in) { g_assert(g_file_set_contents(file, test->in, -1, NULL)); gbinder_config_file = file; } else { gbinder_config_file = NULL; } keyfile = gbinder_config_get(); g_assert(keyfile); /* Compare it against the expected value */ g_assert(g_key_file_load_from_data(expected, test->out, (gsize)-1, G_KEY_FILE_NONE, NULL)); g_assert(test_keyfiles_equal(keyfile, expected)); /* Reset the state again */ gbinder_config_exit(); gbinder_config_file = default_file; remove(file); g_free(file); remove(dir); g_free(dir); g_key_file_unref(expected); } static const TestPresetsData test_presets_data [] = { { "override", "[General]\n" "ApiLevel = 28\n" "[ServiceManager]\n" "/dev/vndbinder = aidl\n", "[General]\n" "ApiLevel = 28\n" "[ServiceManager]\n" "/dev/binder = aidl2\n" "/dev/vndbinder = aidl\n" /* Preset is overridden */ },{ "too_small", "[General]\n" "ApiLevel = 27\n", "[General]\n" "ApiLevel = 27\n" },{ "28", "[General]\n" "ApiLevel = 28", "[General]\n" "ApiLevel = 28\n" "[ServiceManager]\n" "/dev/binder = aidl2\n" "/dev/vndbinder = aidl2\n" },{ "29", "[General]\n" "ApiLevel = 29", "[General]\n" "ApiLevel = 29\n" "[Protocol]\n" "/dev/binder = aidl2\n" "/dev/vndbinder = aidl2\n" "[ServiceManager]\n" "/dev/binder = aidl2\n" "/dev/vndbinder = aidl2\n" },{ "30", "[General]\n" "ApiLevel = 30", "[General]\n" "ApiLevel = 30\n" "[Protocol]\n" "/dev/binder = aidl3\n" "/dev/vndbinder = aidl3\n" "[ServiceManager]\n" "/dev/binder = aidl3\n" "/dev/vndbinder = aidl3\n" },{ "31", "[General]\n" "ApiLevel = 31", "[General]\n" "ApiLevel = 31\n" "[Protocol]\n" "/dev/binder = aidl4\n" "/dev/vndbinder = aidl4\n" "[ServiceManager]\n" "/dev/binder = aidl3\n" "/dev/vndbinder = aidl3\n" },{ "33", "[General]\n" "ApiLevel = 33", "[General]\n" "ApiLevel = 33\n" "[Protocol]\n" "/dev/binder = aidl3\n" "/dev/vndbinder = aidl3\n" "[ServiceManager]\n" "/dev/binder = aidl3\n" "/dev/vndbinder = aidl3\n" } }; /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/config/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { guint i; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("non_exist"), test_non_exit); g_test_add_func(TEST_("dirs"), test_dirs); g_test_add_func(TEST_("bad_config"), test_bad_config); g_test_add_func(TEST_("autorelease"), test_autorelease); for (i = 0; i < G_N_ELEMENTS(test_presets_data); i++) { const TestPresetsData* test = test_presets_data + i; char* path; path = g_strconcat(TEST_("presets/"), test->name, NULL); g_test_add_data_func(path, test, test_presets); g_free(path); } test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_driver/000077500000000000000000000000001526127616700172315ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_driver/Makefile000066400000000000000000000001161526127616700206670ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_driver include ../common/Makefile libgbinder-1.1.54/unit/unit_driver/unit_driver.c000066400000000000000000000134711526127616700217350ustar00rootroot00000000000000/* * Copyright (C) 2018-2023 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_handler.h" #include "gbinder_local_request_p.h" #include "gbinder_output_data.h" #include "gbinder_rpc_protocol.h" #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-driver-XXXXXX"; #define STRICT_MODE_PENALTY_GATHER (0x40 << 16) #define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER) /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { GBinderDriver* driver; const char* dev = GBINDER_DEFAULT_BINDER; g_assert(!gbinder_driver_new("", NULL)); driver = gbinder_driver_new(dev, NULL); g_assert(driver); g_assert(!g_strcmp0(dev, gbinder_driver_dev(driver))); g_assert(gbinder_driver_protocol(driver)); g_assert(gbinder_driver_protocol(driver) == gbinder_rpc_protocol_for_device(dev)); g_assert(gbinder_driver_ref(driver) == driver); gbinder_driver_unref(driver); gbinder_driver_free_buffer(driver, NULL); g_assert(gbinder_driver_io(driver)); g_assert(gbinder_driver_increfs(driver, 0)); g_assert(gbinder_driver_decrefs(driver, 0)); g_assert(gbinder_driver_acquire(driver, 0)); g_assert(gbinder_driver_release(driver, 0)); g_assert(gbinder_driver_enter_looper(driver)); g_assert(gbinder_driver_exit_looper(driver)); g_assert(!gbinder_driver_request_death_notification(driver, NULL)); g_assert(!gbinder_driver_clear_death_notification(driver, NULL)); g_assert(!gbinder_driver_dead_binder_done(NULL, NULL)); gbinder_driver_unref(driver); g_assert(!gbinder_handler_transact(NULL, NULL, NULL, 0, 0, NULL)); g_assert(!gbinder_handler_can_loop(NULL)); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * noop *==========================================================================*/ static void test_noop( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(driver); g_assert(driver); g_assert(fd >= 0); test_binder_br_noop(fd, THIS_THREAD); g_assert(gbinder_driver_poll(driver, NULL) == POLLIN); g_assert(gbinder_driver_read(driver, NULL, NULL) == 0); gbinder_driver_unref(driver); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * local_request *==========================================================================*/ static void test_local_request( void) { static const char iface[] = "test"; static const guint8 rpc_header [] = { TEST_INT32_BYTES(BINDER_RPC_FLAGS), TEST_INT32_BYTES(4), TEST_INT16_BYTES('t'), TEST_INT16_BYTES('e'), TEST_INT16_BYTES('s'), TEST_INT16_BYTES('t'), 0x00, 0x00, 0x00, 0x00 }; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = gbinder_driver_local_request_new(driver, iface); GBinderOutputData* data = gbinder_local_request_data(req); g_assert(data->bytes->len == sizeof(rpc_header)); g_assert(!memcmp(data->bytes->data, rpc_header, sizeof(rpc_header))); gbinder_local_request_unref(req); gbinder_driver_unref(driver); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/driver/" int main(int argc, char* argv[]) { TestConfig test_config; int result; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "basic", test_basic); g_test_add_func(TEST_PREFIX "noop", test_noop); g_test_add_func(TEST_PREFIX "local_request", test_local_request); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_eventloop/000077500000000000000000000000001526127616700177515ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_eventloop/Makefile000066400000000000000000000001211526127616700214030ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_eventloop include ../common/Makefile libgbinder-1.1.54/unit/unit_eventloop/unit_eventloop.c000066400000000000000000000166611526127616700232010ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Jolla Ltd. * Copyright (C) 2020-2021 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_eventloop_p.h" static TestOpt test_opt; static int test_eventloop_timeout_add_called; static int test_eventloop_callback_new_called; static int test_eventloop_cleanup_called; static gboolean test_unreached_proc( gpointer data) { g_assert_not_reached(); return G_SOURCE_CONTINUE; } static void test_quit_cb( gpointer data) { g_main_loop_quit((GMainLoop*)data); } /*==========================================================================* * Test event loop integration *==========================================================================*/ static GBinderEventLoopTimeout* test_eventloop_timeout_add( guint interval, GSourceFunc func, gpointer data) { test_eventloop_timeout_add_called++; return NULL; } static void test_eventloop_timeout_remove( GBinderEventLoopTimeout* timeout) { g_assert_not_reached(); } static GBinderEventLoopCallback* test_eventloop_callback_new( GBinderEventLoopCallbackFunc func, gpointer data, GDestroyNotify destroy) { test_eventloop_callback_new_called++; return NULL; } static void test_eventloop_callback_ref( GBinderEventLoopCallback* cb) { g_assert_not_reached(); } static void test_eventloop_callback_unref( GBinderEventLoopCallback* cb) { g_assert_not_reached(); } static void test_eventloop_callback_schedule( GBinderEventLoopCallback* cb) { g_assert_not_reached(); } static void test_eventloop_callback_cancel( GBinderEventLoopCallback* cb) { g_assert_not_reached(); } static void test_eventloop_cleanup( void) { test_eventloop_cleanup_called++; } static const GBinderEventLoopIntegration test_eventloop = { test_eventloop_timeout_add, test_eventloop_timeout_remove, test_eventloop_callback_new, test_eventloop_callback_ref, test_eventloop_callback_unref, test_eventloop_callback_schedule, test_eventloop_callback_cancel, test_eventloop_cleanup }; /*==========================================================================* * replace *==========================================================================*/ static void test_replace( void) { test_eventloop_timeout_add_called = 0; test_eventloop_callback_new_called = 0; test_eventloop_cleanup_called = 0; gbinder_eventloop_set(NULL); gbinder_eventloop_set(&test_eventloop); g_assert(!gbinder_timeout_add(0, test_unreached_proc, NULL)); g_assert_cmpint(test_eventloop_timeout_add_called, == ,1); g_assert(!gbinder_idle_add(test_unreached_proc, NULL)); g_assert_cmpint(test_eventloop_timeout_add_called, == ,2); gbinder_timeout_remove(NULL); g_assert(!gbinder_idle_callback_new(NULL, NULL, NULL)); g_assert_cmpint(test_eventloop_callback_new_called, == ,1); g_assert(!gbinder_idle_callback_ref(NULL)); gbinder_idle_callback_unref(NULL); gbinder_idle_callback_schedule(NULL); gbinder_idle_callback_cancel(NULL); gbinder_eventloop_set(NULL); g_assert_cmpint(test_eventloop_cleanup_called, == ,1); } /*==========================================================================* * idle *==========================================================================*/ static gboolean test_quit_func( gpointer data) { g_main_loop_quit((GMainLoop*)data); return G_SOURCE_REMOVE; } static void test_idle( void) { GMainLoop* loop = g_main_loop_new(NULL, FALSE); gbinder_eventloop_set(NULL); g_assert(gbinder_idle_add(test_quit_func, loop)); test_run(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * timeout *==========================================================================*/ static void test_timeout( void) { GMainLoop* loop = g_main_loop_new(NULL, FALSE); gbinder_eventloop_set(NULL); g_assert(gbinder_timeout_add(10, test_quit_func, loop)); test_run(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * callback *==========================================================================*/ static void test_callback( void) { GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderEventLoopCallback* cb; gbinder_eventloop_set(NULL); cb = gbinder_idle_callback_new(test_quit_cb, loop, NULL); g_assert(gbinder_idle_callback_ref(cb) == cb); gbinder_idle_callback_unref(cb); gbinder_idle_callback_schedule(cb); test_run(&test_opt, loop); gbinder_idle_callback_unref(cb); g_main_loop_unref(loop); } /*==========================================================================* * invoke *==========================================================================*/ static void test_invoke( void) { GMainLoop* loop = g_main_loop_new(NULL, FALSE); gbinder_eventloop_set(NULL); gbinder_idle_callback_invoke_later(test_quit_cb, loop, NULL); test_run(&test_opt, loop); gbinder_eventloop_set(NULL); gbinder_idle_callback_invoke_later(NULL, loop, test_quit_cb); test_run(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/eventloop/" t int main(int argc, char* argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("replace"), test_replace); g_test_add_func(TEST_("idle"), test_idle); g_test_add_func(TEST_("timeout"), test_timeout); g_test_add_func(TEST_("callback"), test_callback); g_test_add_func(TEST_("invoke"), test_invoke); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_fmq/000077500000000000000000000000001526127616700165215ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_fmq/Makefile000066400000000000000000000001131526127616700201540ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_fmq include ../common/Makefile libgbinder-1.1.54/unit/unit_fmq/unit_fmq.c000066400000000000000000000510141526127616700205100ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_fmq_p.h" #if GBINDER_FMQ_SUPPORTED #include "gbinder_log.h" #include #include #include #include static TestOpt test_opt; typedef struct test_fmq_data { const char* name; gsize item_size; gsize max_num_items; gint type; gint flags; gint fd; gsize buffer_size; } TestFmqData; /*==========================================================================* * null *==========================================================================*/ static const TestFmqData test_fmq_tests_null[] = { { "wrong_size", 0, 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 }, { "wrong_count", sizeof(guint32), 0, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 }, { "wrong_buffer_size", sizeof(guint32), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, 1, 0 }, }; static void test_null( gconstpointer test_data) { const TestFmqData* test = test_data; GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items, test->type, test->flags, test->fd, test->buffer_size); g_assert(!fmq); g_assert(!gbinder_fmq_ref(fmq)); gbinder_fmq_unref(fmq); g_assert(gbinder_fmq_available_to_read(fmq) == 0); g_assert(gbinder_fmq_available_to_write(fmq) == 0); g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 0); g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 0); g_assert(!gbinder_fmq_begin_read(fmq, 1)); g_assert(!gbinder_fmq_begin_read((GBinderFmq*)0x1, 0)); g_assert(!gbinder_fmq_begin_write(fmq, 1)); g_assert(!gbinder_fmq_begin_write((GBinderFmq*)0x1, 0)); g_assert(!gbinder_fmq_read(fmq, (void*)0x1, 1)); g_assert(!gbinder_fmq_read((GBinderFmq*)0x1, NULL, 1)); g_assert(!gbinder_fmq_read((GBinderFmq*)0x1, (void*)0x1, 0)); g_assert(!gbinder_fmq_write(fmq, (const void*)0x1, 1)); g_assert(!gbinder_fmq_write((GBinderFmq*)0x1, NULL, 1)); g_assert(!gbinder_fmq_write((GBinderFmq*)0x1, (const void*)0x1, 0)); g_assert(gbinder_fmq_wait_timeout(fmq, 0, (guint32*)0x1, 0) == -EINVAL); g_assert(gbinder_fmq_wait_timeout((GBinderFmq*)0x1, 0, NULL, 0) == -EINVAL); g_assert(gbinder_fmq_wait(fmq, 0, (guint32*)0x1) == -EINVAL); g_assert(gbinder_fmq_wait((GBinderFmq*)0x1, 0, NULL) == -EINVAL); g_assert(gbinder_fmq_wake(fmq, 0) == -EINVAL); } /*==========================================================================* * read/write guint8 *==========================================================================*/ static const TestFmqData test_fmq_tests_read_write_guint8[] = { { "event_flag", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 }, { "no_event_flag", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 }, { "no_reset", sizeof(guint8), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG | GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 }, { "unsync", sizeof(guint8), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 }, { "unsync_no_reset", sizeof(guint8), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG | GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 }, }; static void test_read_write_guint8( gconstpointer test_data) { const TestFmqData* test = test_data; guint i; guint8 in_data[test->max_num_items]; guint8 out_data[test->max_num_items]; GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items, test->type, test->flags, test->fd, test->buffer_size); g_assert(fmq); /* Intiailize input data with random numbers */ for (i = 0; i < test->max_num_items; ++i) { in_data[i] = g_random_int() % G_MAXUINT8; } memset(out_data, 0, test->max_num_items); /* Write data one value at a time */ for (i = 0; i < test->max_num_items; ++i) { g_assert(gbinder_fmq_write(fmq, &in_data[i], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == i + 1); } /* Try to write one item to full buffer * only sync write fails if buffer is full */ if (test->type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) { g_assert(!gbinder_fmq_write(fmq, &in_data[0], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items); } /* Read data one value at a time */ for (i = 0; i < test->max_num_items; ++i) { g_assert(gbinder_fmq_read(fmq, &out_data, 1)); g_assert(out_data[0] == in_data[i]); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items - i - 1); } /* Try to read when there is no data */ g_assert(!gbinder_fmq_read(fmq, &out_data, 1)); memset(out_data, 0, test->max_num_items); /* Fill whole buffer with data */ g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items)); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items); /* Read whole buffer */ g_assert(gbinder_fmq_read(fmq, &out_data, test->max_num_items)); g_assert(!memcmp(in_data, out_data, test->max_num_items * test->item_size)); memset(out_data, 0, test->max_num_items); /* Try to write too many items */ g_assert(!gbinder_fmq_write(fmq, in_data, test->max_num_items + 1)); /* Overwrite unsync queue */ if (test->type == GBINDER_FMQ_TYPE_UNSYNC_WRITE) { g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items)); g_assert(gbinder_fmq_write(fmq, in_data, 1)); /* Writing too much data to unsync queue causes read pointer to reset * on next read */ g_assert(!gbinder_fmq_read(fmq, &out_data, 1)); g_assert(gbinder_fmq_available_to_read(fmq) == 0); } gbinder_fmq_unref(fmq); } /*==========================================================================* * read/write gint64 *==========================================================================*/ static const TestFmqData test_fmq_tests_read_write_gint64[] = { { "event_flag", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 }, { "no_event_flag", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0 }, { "no_reset", sizeof(gint64), 8, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG | GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 }, { "unsync", sizeof(gint64), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0 }, { "unsync_no_reset", sizeof(guint64), 8, GBINDER_FMQ_TYPE_UNSYNC_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG | GBINDER_FMQ_FLAG_NO_RESET_POINTERS, -1, 0 }, }; static void test_read_write_gint64( gconstpointer test_data) { const TestFmqData* test = test_data; guint i; gint64 in_data[test->max_num_items]; gint64 out_data[test->max_num_items]; GBinderFmq* fmq = gbinder_fmq_new(test->item_size, test->max_num_items, test->type, test->flags, test->fd, test->buffer_size); g_assert(fmq); /* Intiailize input data with random numbers */ for (i = 0; i < test->max_num_items; ++i) { in_data[i] = g_random_int(); } memset(out_data, 0, test->max_num_items); /* Write data one value at a time */ for (i = 0; i < test->max_num_items; ++i) { g_assert(gbinder_fmq_write(fmq, &in_data[i], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == i + 1); } /* Try to write one item to full buffer * only sync write fails if buffer is full */ if (test->type == GBINDER_FMQ_TYPE_SYNC_READ_WRITE) { g_assert(!gbinder_fmq_write(fmq, &in_data[0], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items); } /* Read data one value at a time */ for (i = 0; i < test->max_num_items; ++i) { g_assert(gbinder_fmq_read(fmq, &out_data, 1)); g_assert(out_data[0] == in_data[i]); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items - i - 1); } /* Try to read when there is no data */ g_assert(!gbinder_fmq_read(fmq, &out_data, 1)); memset(out_data, 0, test->max_num_items); /* Fill whole buffer with data */ g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items)); g_assert(gbinder_fmq_available_to_read(fmq) == test->max_num_items); /* Read whole buffer */ g_assert(gbinder_fmq_read(fmq, &out_data, test->max_num_items)); g_assert(!memcmp(in_data, out_data, test->max_num_items * test->item_size)); memset(out_data, 0, test->max_num_items); /* Try to write too many items */ g_assert(!gbinder_fmq_write(fmq, in_data, test->max_num_items + 1)); /* Overwrite unsync queue */ if (test->type == GBINDER_FMQ_TYPE_UNSYNC_WRITE) { g_assert(gbinder_fmq_write(fmq, in_data, test->max_num_items)); g_assert(gbinder_fmq_write(fmq, in_data, 1)); /* Writing too much data to unsync queue causes read pointer to reset * on next read */ g_assert(!gbinder_fmq_read(fmq, &out_data, 1)); g_assert(gbinder_fmq_available_to_read(fmq) == 0); } gbinder_fmq_unref(fmq); } /*==========================================================================* * read/write counters *==========================================================================*/ static void test_read_write_counters( void) { gint max_num_items = 8; gint write_count = 6; gint64 in_data[max_num_items]; gint64 out_data[max_num_items]; guint i; GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), max_num_items, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); g_assert(fmq); /* Intiailize input data with random numbers */ for (i = 0; i < max_num_items; ++i) { in_data[i] = g_random_int(); } memset(out_data, 0, max_num_items); /* Write data one value at a time */ for (i = 0; i < write_count; ++i) { g_assert(gbinder_fmq_write(fmq, &in_data[i], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == i + 1); g_assert(gbinder_fmq_available_to_write(fmq) == max_num_items - i - 1); } /* Read data one value at a time */ for (i = 0; i < 2; ++i) { g_assert(gbinder_fmq_read(fmq, &out_data, 1)); g_assert(out_data[0] == in_data[i]); g_assert(gbinder_fmq_available_to_read(fmq) == write_count - i - 1); g_assert(gbinder_fmq_available_to_write(fmq) == max_num_items - write_count + i + 1); g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == max_num_items - write_count); } g_assert(gbinder_fmq_read(fmq, &out_data, 2)); g_assert(gbinder_fmq_available_to_read(fmq) == 2); g_assert(gbinder_fmq_available_to_write(fmq) == 6); g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 2); g_assert(gbinder_fmq_write(fmq, in_data, 4)); g_assert(gbinder_fmq_available_to_read(fmq) == 6); g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 4); g_assert(gbinder_fmq_read(fmq, &out_data, 6)); g_assert(gbinder_fmq_available_to_read(fmq) == 0); g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 0); g_assert(gbinder_fmq_write(fmq, in_data, 6)); g_assert(gbinder_fmq_available_to_read(fmq) == 6); g_assert(gbinder_fmq_available_to_read_contiguous(fmq) == 6); g_assert(gbinder_fmq_available_to_write(fmq) == 2); g_assert(gbinder_fmq_available_to_write_contiguous(fmq) == 2); gbinder_fmq_unref(fmq); } /*==========================================================================* * read/write external fd *==========================================================================*/ static void test_read_write_external_fd( void) { gint max_num_items = 8; gint64 in_data[max_num_items]; gint64 out_data[max_num_items]; gsize item_size = sizeof(gint64); guint i; GBinderFmq* fmq; /* Allocate shared memory */ int shmem_fd = syscall(__NR_memfd_create, "MessageQueue", MFD_CLOEXEC); g_assert(shmem_fd >= 0); g_assert(ftruncate(shmem_fd, max_num_items * item_size) >= 0); fmq = gbinder_fmq_new(item_size, max_num_items, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, shmem_fd, max_num_items * item_size); g_assert(fmq); /* Intiailize input data with random numbers */ for (i = 0; i < max_num_items; ++i) { in_data[i] = g_random_int(); } memset(out_data, 0, max_num_items); /* Write data one value at a time */ for (i = 0; i < max_num_items; ++i) { g_assert(gbinder_fmq_write(fmq, &in_data[i], 1)); g_assert(gbinder_fmq_available_to_read(fmq) == i + 1); } /* Read data one value at a time */ for (i = 0; i < max_num_items; ++i) { g_assert(gbinder_fmq_read(fmq, &out_data, 1)); g_assert(out_data[0] == in_data[i]); g_assert(gbinder_fmq_available_to_read(fmq) == max_num_items - i - 1); } gbinder_fmq_unref(fmq); } /*==========================================================================* * ref/unref *==========================================================================*/ static void test_ref( void) { GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), 2, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); g_assert(fmq); g_assert(gbinder_fmq_ref(fmq) == fmq); gbinder_fmq_unref(fmq); gbinder_fmq_unref(fmq); } /*==========================================================================* * wait/wake *==========================================================================*/ static void test_wait_wake( void) { gint ms = 100; guint32 state = 0; int result = 0; /* Queue with event flag */ GBinderFmq* fmq = gbinder_fmq_new(sizeof(gint64), 2, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); g_assert(fmq); /* Wait until timeout */ g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms), ==, -ETIMEDOUT); /* Invalid bit mask */ g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x0, &state, ms),==,-EINVAL); /* Bit already set */ result = gbinder_fmq_wake(fmq, 0x4); g_assert(result == 0 || result == -ENOSYS); /* Only run wake/wait tests if FUTEX_WAKE_BITSET is supported */ if (result == 0) { g_assert_cmpint(gbinder_fmq_wait(fmq, 0x4, &state),==,0); g_assert_cmpuint(state,==,0x4); /* Bit already set, wait with more generic bit mask */ state = 0; g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,0); g_assert_cmpint(gbinder_fmq_wait(fmq, 0xf, &state),==,0); g_assert_cmpuint(state,==,0x4); /* Bit already set, wait with different bit mask */ state = 0; g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,0); g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms), ==, -ETIMEDOUT); g_assert_cmpint(gbinder_fmq_try_wait(fmq, 0x2, &state), == , -ETIMEDOUT); } gbinder_fmq_unref(fmq); /* Queue without event flag */ fmq = gbinder_fmq_new(sizeof(gint64), 2, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, 0, -1, 0); g_assert(fmq); g_assert_cmpint(gbinder_fmq_wait_timeout(fmq, 0x2, &state, ms),==,-ENOSYS); g_assert_cmpint(gbinder_fmq_wake(fmq, 0x4),==,-ENOSYS); gbinder_fmq_unref(fmq); } /*==========================================================================* * zero copy *==========================================================================*/ static void test_zero_copy( void) { gint max_num_items = 8; gint write_count = 2; gint64 in_data[max_num_items]; gint64 out_data[max_num_items]; gsize item_size = sizeof(gint64); guint i; GBinderFmq* fmq = gbinder_fmq_new(item_size, max_num_items, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); const void *read_ptr; void *write_ptr; g_assert(fmq); /* Intiailize input data with random numbers */ for (i = 0; i < max_num_items; ++i) { in_data[i] = g_random_int(); } memset(out_data, 0, max_num_items); /* external write */ g_assert_nonnull((write_ptr = gbinder_fmq_begin_write(fmq, write_count))); memcpy(write_ptr, in_data, write_count * item_size); gbinder_fmq_end_write(fmq, write_count); g_assert(gbinder_fmq_available_to_read(fmq) == write_count); g_assert(gbinder_fmq_available_to_write(fmq) == (max_num_items - write_count)); /* external read */ g_assert_nonnull((read_ptr = gbinder_fmq_begin_read(fmq, write_count))); memcpy(out_data, read_ptr, write_count * item_size); gbinder_fmq_end_read(fmq, write_count); g_assert(!memcmp(in_data, out_data, write_count * item_size)); gbinder_fmq_unref(fmq); } #endif /* GBINDER_FMQ_SUPPORTED */ /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/fmq/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { #if GBINDER_FMQ_SUPPORTED guint i; int test_fd; g_test_init(&argc, &argv, NULL); test_init(&test_opt, argc, argv); gbinder_log.level = gutil_log_default.level; for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_null); i++) { const TestFmqData* test = test_fmq_tests_null + i; char* path = g_strconcat(TEST_PREFIX, test->name, NULL); g_test_add_data_func(path, test, test_null); g_free(path); } /* Some test environments don't know how handle this syscall */ test_fd = syscall(__NR_memfd_create, "test", MFD_CLOEXEC); if (test_fd < 0 && errno == ENOSYS) { GINFO("Skipping tests that rely on memfd_create"); } else { close(test_fd); for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_read_write_guint8); i++) { const TestFmqData* test = test_fmq_tests_read_write_guint8 + i; char* path = g_strconcat(TEST_("guint8/"), test->name, NULL); g_test_add_data_func(path, test, test_read_write_guint8); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_fmq_tests_read_write_gint64); i++) { const TestFmqData* test = test_fmq_tests_read_write_gint64 + i; char* path = g_strconcat(TEST_("gint64/"), test->name, NULL); g_test_add_data_func(path, test, test_read_write_gint64); g_free(path); } g_test_add_func(TEST_("read_write_counters"), test_read_write_counters); g_test_add_func(TEST_("read_write_external_fd"), test_read_write_external_fd); g_test_add_func(TEST_("ref"), test_ref); g_test_add_func(TEST_("wait_wake"), test_wait_wake); g_test_add_func(TEST_("zero_copy"), test_zero_copy); } #else /* GBINDER_FMQ_SUPPORTED */ g_test_init(&argc, &argv, NULL); #endif return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_ipc/000077500000000000000000000000001526127616700165115ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_ipc/Makefile000066400000000000000000000001131526127616700201440ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_ipc include ../common/Makefile libgbinder-1.1.54/unit/unit_ipc/unit_ipc.c000066400000000000000000001215331526127616700204740ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_ipc.h" #include "gbinder_driver.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_local_request_p.h" #include "gbinder_object_registry.h" #include "gbinder_output_data.h" #include "gbinder_remote_reply.h" #include "gbinder_remote_request.h" #include "gbinder_rpc_protocol.h" #include "gbinder_writer.h" #include #include #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-ipc-XXXXXX"; static gboolean test_unref_ipc( gpointer ipc) { gbinder_ipc_unref(ipc); return G_SOURCE_REMOVE; } static void test_quit_when_destroyed( gpointer loop, GObject* obj) { test_quit_later((GMainLoop*)loop); } static GBinderLocalRequest* test_local_request_new( GBinderIpc* ipc) { return gbinder_local_request_new(gbinder_driver_io(ipc->driver), gbinder_driver_protocol(ipc->driver), NULL); } static GBinderLocalReply* test_local_reply_new( GBinderIpc* ipc) { return gbinder_local_reply_new(gbinder_driver_io(ipc->driver), gbinder_driver_protocol(ipc->driver)); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { int status = INT_MAX; g_assert(!gbinder_ipc_ref(NULL)); gbinder_ipc_unref(NULL); g_assert(!gbinder_ipc_sync_main.sync_reply(NULL, 0, 0, NULL, NULL)); g_assert(!gbinder_ipc_sync_main.sync_reply(NULL, 0, 0, NULL, &status)); g_assert_cmpint(status, == ,-EINVAL); g_assert(!gbinder_ipc_sync_worker.sync_reply(NULL, 0, 0, NULL, NULL)); g_assert(!gbinder_ipc_sync_worker.sync_reply(NULL, 0, 0, NULL, &status)); g_assert_cmpint(status, == ,-EINVAL); g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(NULL, 0, 0, NULL), == , -EINVAL); g_assert_cmpint(gbinder_ipc_sync_worker.sync_oneway(NULL, 0, 0, NULL), == , -EINVAL); g_assert(!gbinder_ipc_transact(NULL, 0, 0, 0, NULL, NULL, NULL, NULL)); g_assert(!gbinder_ipc_transact_custom(NULL, NULL, NULL, NULL, NULL)); g_assert(!gbinder_ipc_object_registry(NULL)); gbinder_ipc_looper_check(NULL); gbinder_ipc_cancel(NULL, 0); g_assert(!gbinder_object_registry_ref(NULL)); gbinder_object_registry_unref(NULL); g_assert(!gbinder_object_registry_get_local(NULL, NULL)); g_assert(!gbinder_object_registry_get_remote(NULL, 0, FALSE)); g_assert(!gbinder_ipc_find_local_object(NULL, NULL, NULL)); } /*==========================================================================* * basic *==========================================================================*/ static gboolean test_basic_find_none( GBinderLocalObject* obj, void* user_data) { return FALSE; } static gboolean test_basic_find( GBinderLocalObject* obj, void* user_data) { return obj == user_data; } static void test_basic( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderLocalObject* obj; g_assert(ipc); g_assert(ipc2); g_assert(ipc != ipc2); gbinder_ipc_cancel(ipc2, 0); /* not a valid transaction */ gbinder_ipc_unref(ipc2); g_assert(!gbinder_ipc_find_local_object(NULL, test_basic_find_none, NULL)); g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL)); obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); g_assert(obj); g_assert(!gbinder_ipc_find_local_object(ipc, test_basic_find_none, NULL)); g_assert(gbinder_ipc_find_local_object(ipc, test_basic_find, obj) == obj); gbinder_local_object_unref(obj); /* Above call added a reference */ gbinder_local_object_unref(obj); /* Second gbinder_ipc_new returns the same (default) object */ g_assert(gbinder_ipc_new(NULL, NULL) == ipc); g_assert(gbinder_ipc_new("", NULL) == ipc); gbinder_ipc_unref(ipc); gbinder_ipc_unref(ipc); gbinder_ipc_unref(ipc); /* Invalid path */ g_assert(!gbinder_ipc_new("invalid path", NULL)); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * protocol *==========================================================================*/ static void test_protocol( void) { /* GBinderIpc objects are identified by device + protocol combination */ GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, "aidl"); GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, "hidl"); g_assert(ipc); g_assert(ipc2); g_assert(ipc != ipc2); gbinder_ipc_unref(ipc); gbinder_ipc_unref(ipc2); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * async_oneway *==========================================================================*/ static void test_async_oneway_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { g_assert(!status); g_assert(!reply); test_quit_later((GMainLoop*)user_data); } static void test_async_oneway( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; test_binder_br_transaction_complete(fd, TX_THREAD); id = gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY, req, test_async_oneway_done, NULL, loop); g_assert(id); test_run(&test_opt, loop); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); g_main_loop_unref(loop); } /*==========================================================================* * sync_oneway *==========================================================================*/ static void test_sync_oneway( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); test_binder_br_transaction_complete(fd, THIS_THREAD); g_assert_cmpint(gbinder_ipc_sync_main.sync_oneway(ipc, 0, 1, req), == ,0); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * sync_reply_ok *==========================================================================*/ static void test_sync_reply_ok_status( int* status) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderLocalReply* reply = test_local_reply_new(ipc); GBinderRemoteReply* tx_reply; GBinderOutputData* data; const int fd = gbinder_driver_fd(ipc->driver); const guint32 handle = 0; const guint32 code = 1; const char* result_in = "foo"; char* result_out; g_assert(gbinder_local_reply_append_string16(reply, result_in)); data = gbinder_local_reply_data(reply); g_assert(data); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, handle, code, data->bytes); tx_reply = gbinder_ipc_sync_main.sync_reply(ipc, handle, code, req, status); g_assert(tx_reply); result_out = gbinder_remote_reply_read_string16(tx_reply); g_assert(!g_strcmp0(result_out, result_in)); g_free(result_out); gbinder_remote_reply_unref(tx_reply); gbinder_local_request_unref(req); gbinder_local_reply_unref(reply); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } static void test_sync_reply_ok( void) { int status = -1; test_sync_reply_ok_status(NULL); test_sync_reply_ok_status(&status); g_assert(status == GBINDER_STATUS_OK); } /*==========================================================================* * sync_reply_error *==========================================================================*/ static void test_sync_reply_error( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); const guint32 handle = 0; const guint32 code = 1; const gint expected_status = (-EINVAL); const gint unexpected_status = GBINDER_STATUS_FAILED; int status = INT_MAX; test_binder_ignore_dead_object(fd); test_binder_br_noop(fd, TX_THREAD); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply_status(fd, TX_THREAD, expected_status); g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status)); g_assert_cmpint(status, == ,expected_status); /* GBINDER_STATUS_FAILED gets replaced with -EFAULT */ test_binder_ignore_dead_object(fd); test_binder_br_noop(fd, TX_THREAD); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply_status(fd, TX_THREAD, unexpected_status); g_assert(!gbinder_ipc_sync_main.sync_reply(ipc,handle,code,req,&status)); g_assert_cmpint(status, == ,-EFAULT); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * transact_ok *==========================================================================*/ #define TEST_REQ_PARAM_STR "foo" static void test_transact_ok_destroy( void* user_data) { test_quit_later((GMainLoop*)user_data); } static void test_transact_ok_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { char* result; GVERBOSE_(""); result = gbinder_remote_reply_read_string16(reply); g_assert(!g_strcmp0(result, TEST_REQ_PARAM_STR)); g_free(result); g_assert(status == GBINDER_STATUS_OK); } static void test_transact_ok( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderLocalReply* reply = test_local_reply_new(ipc); GBinderOutputData* data; const guint32 handle = 0; const guint32 code = 1; const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR)); data = gbinder_local_reply_data(reply); g_assert(data); test_binder_br_noop(fd, TX_THREAD); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply(fd, TX_THREAD, handle, code, data->bytes); id = gbinder_ipc_transact(ipc, handle, code, 0, req, test_transact_ok_done, test_transact_ok_destroy, loop); g_assert(id); test_run(&test_opt, loop); /* Transaction id is not valid anymore: */ gbinder_ipc_cancel(ipc, id); gbinder_local_request_unref(req); gbinder_local_reply_unref(reply); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_dead *==========================================================================*/ static void test_transact_dead_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { GVERBOSE_("%d", status); g_assert(!reply); g_assert(status == GBINDER_STATUS_DEAD_OBJECT); test_quit_later((GMainLoop*)user_data); } static void test_transact_dead( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; test_binder_br_noop(fd, TX_THREAD); test_binder_br_dead_reply(fd, TX_THREAD); id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_dead_done, NULL, loop); g_assert(id); test_run(&test_opt, loop); /* Transaction id is not valid anymore: */ gbinder_ipc_cancel(ipc, id); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_failed *==========================================================================*/ static void test_transact_failed_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { GVERBOSE_("%d", status); g_assert(!reply); g_assert(status == GBINDER_STATUS_FAILED); test_quit_later((GMainLoop*)user_data); } static void test_transact_failed( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; test_binder_br_noop(fd, TX_THREAD); test_binder_br_failed_reply(fd, TX_THREAD); id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_failed_done, NULL, loop); g_assert(id); test_run(&test_opt, loop); /* Transaction id is not valid anymore: */ gbinder_ipc_cancel(ipc, id); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_status *==========================================================================*/ #define EXPECTED_STATUS (0x42424242) static void test_transact_status_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { GVERBOSE_("%d", status); g_assert(!reply); g_assert(status == EXPECTED_STATUS); test_quit_later((GMainLoop*)user_data); } static void test_transact_status( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id; test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply_status(fd, TX_THREAD, EXPECTED_STATUS); id = gbinder_ipc_transact(ipc, 1, 2, 0, req, test_transact_status_done, NULL, loop); g_assert(id); test_run(&test_opt, loop); /* Transaction id is not valid anymore: */ gbinder_ipc_cancel(ipc, id); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_custom *==========================================================================*/ static void test_transact_custom_done( const GBinderIpcTx* tx) { GVERBOSE_(""); test_quit_later((GMainLoop*)tx->user_data); } static void test_transact_custom( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id = gbinder_ipc_transact_custom(ipc, NULL, test_transact_custom_done, NULL, loop); g_assert(id); test_run(&test_opt, loop); gbinder_ipc_exit(); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_custom2 *==========================================================================*/ static void test_transact_custom_destroy( void* user_data) { GVERBOSE_(""); test_quit_later((GMainLoop*)user_data); } static void test_transact_custom2( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id = gbinder_ipc_transact_custom(ipc, NULL, NULL, test_transact_custom_destroy, loop); g_assert(id); test_run(&test_opt, loop); gbinder_ipc_exit(); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_custom3 *==========================================================================*/ static void test_transact_custom3_exec( const GBinderIpcTx* tx) { GVERBOSE_(""); gbinder_ipc_unref(tx->ipc); test_quit_later((GMainLoop*)tx->user_data); } static void test_transact_custom3( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */ gulong id = gbinder_ipc_transact_custom(ipc, test_transact_custom3_exec, NULL, NULL, loop); g_assert(id); test_run(&test_opt, loop); /* Reference to GBinderIpc is released by test_transact_custom3_exec */ test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_cancel *==========================================================================*/ static void test_transact_cancel_destroy( void* user_data) { GVERBOSE_(""); test_quit_later((GMainLoop*)user_data); } static void test_transact_cancel_exec( const GBinderIpcTx* tx) { GVERBOSE_(""); } static void test_transact_cancel_done( const GBinderIpcTx* tx) { GVERBOSE_(""); g_assert(tx->cancelled); } static void test_transact_cancel( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel_exec, test_transact_cancel_done, test_transact_cancel_destroy, loop); g_assert(id); gbinder_ipc_cancel(ipc, id); test_run(&test_opt, loop); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_cancel2 *==========================================================================*/ static gboolean test_transact_cancel2_cancel( gpointer data) { const GBinderIpcTx* tx = data; GVERBOSE_(""); gbinder_ipc_cancel(tx->ipc, tx->id); return G_SOURCE_REMOVE; } static void test_transact_cancel2_exec( const GBinderIpcTx* tx) { GVERBOSE_(""); g_assert(!tx->cancelled); g_main_context_invoke(NULL, test_transact_cancel2_cancel, (void*)tx); } static void test_transact_cancel2( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); /* Reusing test_transact_cancel_done and test_transact_cancel_destroy */ gulong id = gbinder_ipc_transact_custom(ipc, test_transact_cancel2_exec, test_transact_cancel_done, test_transact_cancel_destroy, loop); g_assert(id); test_run(&test_opt, loop); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * transact_2way *==========================================================================*/ static GBinderLocalReply* test_transact_2way_incoming_proc( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* incoming_call = user_data; GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code); g_assert_cmpuint(flags, == ,0); g_assert_cmpint(gbinder_remote_request_sender_pid(req), == ,getpid()); g_assert_cmpint(gbinder_remote_request_sender_euid(req), == ,geteuid()); g_assert_cmpstr(gbinder_remote_request_interface(req), == ,"test"); g_assert_cmpstr(gbinder_remote_request_read_string8(req), == ,"message"); g_assert_cmpuint(code, == ,2); g_assert_cmpint(*incoming_call, == ,0); (*incoming_call)++; *status = GBINDER_STATUS_OK; return gbinder_local_object_new_reply(obj); } static void test_transact_2way_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* dev = gbinder_driver_dev(ipc->driver); const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "test", NULL }; const guint32 handle = 0; const guint32 code = 1; int incoming_call = 0; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj = gbinder_local_object_new (ipc, ifaces, test_transact_2way_incoming_proc, &incoming_call); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderLocalRequest* incoming_req = test_local_request_new(ipc); GBinderLocalReply* reply = test_local_reply_new(ipc); GBinderWriter writer; /* Prepare reply */ g_assert(gbinder_local_reply_append_string16(reply, TEST_REQ_PARAM_STR)); /* Prepare incoming request */ gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, "test"); gbinder_writer_append_string8(&writer, "message"); test_binder_ignore_dead_object(fd); test_binder_br_transaction(fd, TX_THREAD, obj, 2, gbinder_local_request_data(req)->bytes); test_binder_br_noop(fd, TX_THREAD); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_noop(fd, TX_THREAD); test_binder_br_reply(fd, TX_THREAD, handle, code, gbinder_local_reply_data(reply)->bytes); /* NB. Reusing test_transact_ok_done and test_transact_ok_destroy */ g_assert(gbinder_ipc_transact(ipc, handle, code, 0, req, test_transact_ok_done, test_transact_ok_destroy, loop)); test_run(&test_opt, loop); /* Now we need to wait until GBinderIpc is destroyed */ GDEBUG("waiting for GBinderIpc to get destroyed"); g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop); gbinder_local_object_unref(obj); gbinder_local_request_unref(req); gbinder_local_request_unref(incoming_req); gbinder_local_reply_unref(reply); g_idle_add(test_unref_ipc, ipc); test_run(&test_opt, loop); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_2way( void) { test_run_in_context(&test_opt, test_transact_2way_run); } /*==========================================================================* * transact_unhandled *==========================================================================*/ static void test_transact_unhandled_done( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { g_assert(!reply); g_assert_cmpint(status, == ,GBINDER_STATUS_DEAD_OBJECT); test_quit_later((GMainLoop*)user_data); } static void test_transact_unhandled_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderDriver* driver = ipc->driver; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalRequest* req = gbinder_driver_local_request_new_ping(driver); g_assert(gbinder_ipc_transact(ipc, 1 /* Non-existent object */, gbinder_driver_protocol(driver)->ping_tx, 0, req, test_transact_unhandled_done, NULL, loop)); gbinder_local_request_unref(req); test_run(&test_opt, loop); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_unhandled( void) { test_run_in_context(&test_opt, test_transact_unhandled_run); } /*==========================================================================* * transact_incoming *==========================================================================*/ static GBinderLocalReply* test_transact_incoming_proc( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code); g_assert(!flags); g_assert(gbinder_remote_request_sender_pid(req) == getpid()); g_assert(gbinder_remote_request_sender_euid(req) == geteuid()); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test")); g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message")); g_assert(code == 1); test_quit_later((GMainLoop*)user_data); *status = GBINDER_STATUS_OK; return gbinder_local_object_new_reply(obj); } static void test_transact_incoming_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* dev = gbinder_driver_dev(ipc->driver); const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "test", NULL }; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj = gbinder_local_object_new (ipc, ifaces, test_transact_incoming_proc, loop); GBinderLocalRequest* ping = test_local_request_new(ipc); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderWriter writer; gbinder_local_request_init_writer(ping, &writer); prot->write_ping(&writer); gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, "test"); gbinder_writer_append_string8(&writer, "message"); test_binder_br_transaction(fd, LOOPER_THREAD, obj, prot->ping_tx, gbinder_local_request_data(ping)->bytes); test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */ test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, gbinder_local_request_data(req)->bytes); test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */ test_run(&test_opt, loop); /* Now we need to wait until GBinderIpc is destroyed */ GDEBUG("waiting for GBinderIpc to get destroyed"); g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop); gbinder_local_object_unref(obj); gbinder_local_request_unref(ping); gbinder_local_request_unref(req); g_idle_add(test_unref_ipc, ipc); test_run(&test_opt, loop); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_incoming( void) { test_run_in_context(&test_opt, test_transact_incoming_run); } /*==========================================================================* * transact_status_reply *==========================================================================*/ static GBinderLocalReply* test_transact_status_reply_proc( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code); g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test")); g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message")); g_assert(code == 1); test_quit_later((GMainLoop*)user_data); *status = EXPECTED_STATUS; return NULL; } static void test_transact_status_reply_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* dev = gbinder_driver_dev(ipc->driver); const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "test", NULL }; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj = gbinder_local_object_new (ipc, ifaces, test_transact_status_reply_proc, loop); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, "test"); gbinder_writer_append_string8(&writer, "message"); data = gbinder_local_request_data(req); test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes); test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */ test_run(&test_opt, loop); /* Now we need to wait until GBinderIpc is destroyed */ GDEBUG("waiting for GBinderIpc to get destroyed"); g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop); gbinder_local_object_unref(obj); gbinder_local_request_unref(req); g_idle_add(test_unref_ipc, ipc); test_run(&test_opt, loop); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_status_reply( void) { test_run_in_context(&test_opt, test_transact_status_reply_run); } /*==========================================================================* * transact_async *==========================================================================*/ typedef struct test_transact_async_req { GBinderLocalObject* obj; GBinderRemoteRequest* req; GMainLoop* loop; } TestTransactAsyncReq; static void test_transact_async_done( gpointer data) { TestTransactAsyncReq* test = data; gbinder_local_object_unref(test->obj); gbinder_remote_request_unref(test->req); test_quit_later(test->loop); g_free(test); } static gboolean test_transact_async_reply( gpointer data) { TestTransactAsyncReq* test = data; GBinderLocalReply* reply = gbinder_local_object_new_reply(test->obj); gbinder_remote_request_complete(test->req, reply, 0); gbinder_local_reply_unref(reply); return G_SOURCE_REMOVE; } static GBinderLocalReply* test_transact_async_proc( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* loop) { TestTransactAsyncReq* test = g_new(TestTransactAsyncReq, 1); GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code); g_assert(!flags); g_assert(gbinder_remote_request_sender_pid(req) == getpid()); g_assert(gbinder_remote_request_sender_euid(req) == geteuid()); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test")); g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message")); g_assert(code == 1); test->obj = gbinder_local_object_ref(obj); test->req = gbinder_remote_request_ref(req); test->loop = (GMainLoop*)loop; gbinder_remote_request_block(req); gbinder_remote_request_block(req); /* wrong state; has no effect */ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, test_transact_async_reply, test, test_transact_async_done); return NULL; } static void test_transact_async_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* dev = gbinder_driver_dev(ipc->driver); const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "test", NULL }; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj = gbinder_local_object_new (ipc, ifaces, test_transact_async_proc, loop); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, "test"); gbinder_writer_append_string8(&writer, "message"); data = gbinder_local_request_data(req); test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes); test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */ test_run(&test_opt, loop); /* Now we need to wait until GBinderIpc is destroyed */ GDEBUG("waiting for GBinderIpc to get destroyed"); g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop); gbinder_local_object_unref(obj); gbinder_local_request_unref(req); g_idle_add(test_unref_ipc, ipc); test_run(&test_opt, loop); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_async( void) { test_run_in_context(&test_opt, test_transact_async_run); } /*==========================================================================* * transact_async_sync *==========================================================================*/ static GBinderLocalReply* test_transact_async_sync_proc( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* loop) { GBinderLocalReply* reply = gbinder_local_object_new_reply(obj); GVERBOSE_("\"%s\" %u", gbinder_remote_request_interface(req), code); g_assert(!flags); g_assert(gbinder_remote_request_sender_pid(req) == getpid()); g_assert(gbinder_remote_request_sender_euid(req) == geteuid()); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), "test")); g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "message")); g_assert(code == 1); /* Block and immediately complete the call */ gbinder_remote_request_block(req); gbinder_remote_request_complete(req, reply, 0); gbinder_remote_request_complete(req, reply, 0); /* This one is ignored */ gbinder_local_reply_unref(reply); test_quit_later((GMainLoop*)loop); return NULL; } static void test_transact_async_sync_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* dev = gbinder_driver_dev(ipc->driver); const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "test", NULL }; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj = gbinder_local_object_new (ipc, ifaces, test_transact_async_sync_proc, loop); GBinderLocalRequest* req = test_local_request_new(ipc); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, "test"); gbinder_writer_append_string8(&writer, "message"); data = gbinder_local_request_data(req); test_binder_br_transaction(fd, LOOPER_THREAD, obj, 1, data->bytes); test_binder_br_transaction_complete(fd, LOOPER_THREAD); /* For reply */ test_run(&test_opt, loop); /* Now we need to wait until GBinderIpc is destroyed */ GDEBUG("waiting for GBinderIpc to get destroyed"); g_object_weak_ref(G_OBJECT(ipc), test_quit_when_destroyed, loop); gbinder_local_object_unref(obj); gbinder_local_request_unref(req); g_idle_add(test_unref_ipc, ipc); test_run(&test_opt, loop); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_transact_async_sync( void) { test_run_in_context(&test_opt, test_transact_async_sync_run); } /*==========================================================================* * drop_remote_refs *==========================================================================*/ static void test_drop_remote_refs_cb( GBinderLocalObject* obj, void* user_data) { GVERBOSE_("%d", obj->strong_refs); g_assert(obj->strong_refs == 1); test_quit_later((GMainLoop*)user_data); } static void test_drop_remote_refs_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalObject* obj = gbinder_local_object_new (ipc, NULL, NULL, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj, test_drop_remote_refs_cb, loop); test_binder_br_acquire(fd, ANY_THREAD, obj); test_run(&test_opt, loop); g_assert(obj->strong_refs == 1); gbinder_local_object_remove_handler(obj, id); gbinder_local_object_unref(obj); /* gbinder_ipc_exit will drop the remote reference */ gbinder_ipc_unref(ipc); gbinder_ipc_exit(); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_drop_remote_refs( void) { test_run_in_context(&test_opt, test_drop_remote_refs_run); } /*==========================================================================* * cancel_on_exit *==========================================================================*/ static void test_cancel_on_exit_not_reached( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { g_assert_not_reached(); } static void test_cancel_on_exit( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = test_local_request_new(ipc); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); /* This transaction will be cancelled by gbinder_ipc_exit */ test_binder_br_transaction_complete(fd, TX_THREAD); gbinder_ipc_transact(ipc, 0, 1, GBINDER_TX_FLAG_ONEWAY, req, test_cancel_on_exit_not_reached, NULL, NULL); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); gbinder_ipc_exit(); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/ipc/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { TestConfig test_config; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("protocol"), test_protocol); g_test_add_func(TEST_("async_oneway"), test_async_oneway); g_test_add_func(TEST_("sync_oneway"), test_sync_oneway); g_test_add_func(TEST_("sync_reply_ok"), test_sync_reply_ok); g_test_add_func(TEST_("sync_reply_error"), test_sync_reply_error); g_test_add_func(TEST_("transact_ok"), test_transact_ok); g_test_add_func(TEST_("transact_dead"), test_transact_dead); g_test_add_func(TEST_("transact_failed"), test_transact_failed); g_test_add_func(TEST_("transact_status"), test_transact_status); g_test_add_func(TEST_("transact_custom"), test_transact_custom); g_test_add_func(TEST_("transact_custom2"), test_transact_custom2); g_test_add_func(TEST_("transact_custom3"), test_transact_custom3); g_test_add_func(TEST_("transact_cancel"), test_transact_cancel); g_test_add_func(TEST_("transact_cancel2"), test_transact_cancel2); g_test_add_func(TEST_("transact_2way"), test_transact_2way); g_test_add_func(TEST_("transact_incoming"), test_transact_incoming); g_test_add_func(TEST_("transact_unhandled"), test_transact_unhandled); g_test_add_func(TEST_("transact_status_reply"), test_transact_status_reply); g_test_add_func(TEST_("transact_async"), test_transact_async); g_test_add_func(TEST_("transact_async_sync"), test_transact_async_sync); g_test_add_func(TEST_("drop_remote_refs"), test_drop_remote_refs); g_test_add_func(TEST_("cancel_on_exit"), test_cancel_on_exit); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_local_object/000077500000000000000000000000001526127616700203565ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_local_object/Makefile000066400000000000000000000001241526127616700220130ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_local_object include ../common/Makefile libgbinder-1.1.54/unit/unit_local_object/unit_local_object.c000066400000000000000000000731331526127616700242100ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply_p.h" #include "gbinder_object_registry.h" #include "gbinder_output_data.h" #include "gbinder_reader_p.h" #include "gbinder_remote_request_p.h" #include "gbinder_rpc_protocol.h" #include #include #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-local-object-XXXXXX"; /* android.hidl.base@1.0::IBase */ #define TEST_BASE_INTERFACE_BYTES \ 'a', 'n', 'd', 'r', 'o', 'i', 'd', '.', \ 'h', 'i', 'd', 'l', '.', 'b', 'a', 's', \ 'e', '@', '1', '.', '0', ':', ':', 'I', \ 'B', 'a', 's', 'e' #define TEST_BASE_INTERFACE_HEADER_BYTES \ TEST_BASE_INTERFACE_BYTES, 0x00, 0x00, 0x00, 0x00 static const char base_interface[] = { TEST_BASE_INTERFACE_BYTES, 0x00 }; static void test_reader_data_init_for_reply( GBinderReaderData* data, GBinderLocalObject* obj, GBinderLocalReply* reply) { GBinderIpc* ipc = obj->ipc; GBinderOutputData* out = gbinder_local_reply_data(reply); GUtilIntArray* offsets = gbinder_output_data_offsets(out); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(out->bytes->data, out->bytes->len), out->bytes->len, NULL); memset(data, 0, sizeof(*data)); data->buffer = buf; data->reg = gbinder_object_registry_ref(reg); g_assert(!gbinder_object_registry_get_local(reg, NULL)); g_assert(gbinder_object_registry_get_local(reg, obj) == obj); gbinder_local_object_unref(obj); /* ref added by the above call */ if (offsets && offsets->count > 0) { guint i; data->objects = g_new(void*, offsets->count + 1); for (i = 0; i < offsets->count; i++) { data->objects[i] = (guint8*)buf->data + offsets->data[i]; } data->objects[i] = NULL; } } static void test_reader_data_cleanup( GBinderReaderData* data) { gbinder_object_registry_unref(data->reg); gbinder_buffer_free(data->buffer); g_free(data->objects); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { int status = 0; g_assert(!gbinder_local_object_new(NULL, NULL, NULL, NULL)); g_assert(!gbinder_local_object_ref(NULL)); gbinder_local_object_unref(NULL); gbinder_local_object_drop(NULL); g_assert(!gbinder_local_object_new_reply(NULL)); g_assert(!gbinder_local_object_add_weak_refs_changed_handler(NULL, NULL, NULL)); g_assert(!gbinder_local_object_add_strong_refs_changed_handler(NULL, NULL, NULL)); gbinder_local_object_remove_handler(NULL, 0); g_assert(gbinder_local_object_can_handle_transaction(NULL, NULL, 0) == GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED); g_assert(!gbinder_local_object_handle_transaction(NULL, NULL, 0, 0, NULL)); g_assert(!gbinder_local_object_handle_transaction(NULL, NULL, 0, 0, &status)); g_assert(!gbinder_local_object_handle_looper_transaction(NULL, NULL, 0, 0, NULL)); g_assert(!gbinder_local_object_handle_looper_transaction(NULL, NULL, 0, 0, &status)); g_assert(status == (-EBADMSG)); g_assert(!gbinder_ipc_transact_custom(NULL, NULL, NULL, NULL, NULL)); gbinder_local_object_handle_increfs(NULL); gbinder_local_object_handle_decrefs(NULL); gbinder_local_object_handle_acquire(NULL, NULL); gbinder_local_object_handle_release(NULL); gbinder_local_object_handle_release(NULL); gbinder_local_object_set_stability(NULL, GBINDER_STABILITY_UNDECLARED); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { const char* const ifaces_foo[] = { "foo", NULL }; const char* const ifaces_bar[] = { "bar", NULL }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderLocalObject* foo; GBinderLocalObject* bar; /* ipc is not a local object */ g_assert(!gbinder_object_registry_get_local(reg, ipc)); /* Create a new local objects */ foo = gbinder_local_object_new(ipc, ifaces_foo, NULL, NULL); bar = gbinder_local_object_new(ipc, ifaces_bar, NULL, NULL); /* But ipc is still not a local object! */ g_assert(!gbinder_object_registry_get_local(reg, ipc)); gbinder_ipc_unref(ipc); g_assert(foo); g_assert(!gbinder_local_object_add_weak_refs_changed_handler(foo, NULL, NULL)); g_assert(!gbinder_local_object_add_strong_refs_changed_handler(foo, NULL, NULL)); gbinder_local_object_remove_handler(foo, 0); g_assert(gbinder_local_object_can_handle_transaction(foo, base_interface, -1) == GBINDER_LOCAL_TRANSACTION_NOT_SUPPORTED); gbinder_local_object_handle_increfs(foo); gbinder_local_object_handle_decrefs(foo); gbinder_local_object_handle_acquire(foo, NULL); gbinder_local_object_handle_release(foo); gbinder_local_object_unref(foo); g_assert(bar); g_assert(gbinder_local_object_ref(bar) == bar); gbinder_local_object_drop(bar); gbinder_local_object_unref(bar); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * ping *==========================================================================*/ static void test_ping( void) { int status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); GBinderLocalReply* reply; GBinderOutputData* out_data; static const guint8 result[] = { 0x00, 0x00, 0x00, 0x00 }; g_assert(gbinder_local_object_can_handle_transaction(obj, NULL, GBINDER_PING_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be * handled by handle_looper_transaction() */ g_assert(!gbinder_local_object_handle_transaction(obj, req, GBINDER_PING_TRANSACTION, 0, &status)); g_assert(status == (-EBADMSG)); reply = gbinder_local_object_handle_looper_transaction(obj, req, GBINDER_PING_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); out_data = gbinder_local_reply_data(reply); g_assert(out_data); g_assert(out_data->bytes); g_assert(out_data->bytes->len == sizeof(result)); g_assert(!memcmp(out_data->bytes->data, result, sizeof(result))); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * interface *==========================================================================*/ static void test_interface( void) { int status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); const char* const ifaces[] = { "x", NULL }; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); GBinderLocalReply* reply; GBinderOutputData* out_data; static const guint8 result[] = { TEST_INT32_BYTES(1), TEST_INT16_BYTES('x'), 0x00, 0x00 }; g_assert(gbinder_local_object_can_handle_transaction(obj, NULL, GBINDER_INTERFACE_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be * handled by handle_looper_transaction() */ g_assert(!gbinder_local_object_handle_transaction(obj, req, GBINDER_INTERFACE_TRANSACTION, 0, &status)); g_assert(status == (-EBADMSG)); reply = gbinder_local_object_handle_looper_transaction(obj, req, GBINDER_INTERFACE_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); out_data = gbinder_local_reply_data(reply); g_assert(out_data); g_assert(out_data->bytes); g_assert(out_data->bytes->len == sizeof(result)); g_assert(!memcmp(out_data->bytes->data, result, sizeof(result))); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_ping *==========================================================================*/ static void test_hidl_ping( void) { static const guint8 req_data [] = { TEST_BASE_INTERFACE_HEADER_BYTES }; int status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); GBinderLocalReply* reply; GBinderOutputData* out_data; static const guint8 result[] = { 0x00, 0x00, 0x00, 0x00 }; gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION, gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface)); g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface, HIDL_PING_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be * handled by handle_looper_transaction() */ g_assert(!gbinder_local_object_handle_transaction(obj, req, HIDL_PING_TRANSACTION, 0, &status)); g_assert(status == (-EBADMSG)); reply = gbinder_local_object_handle_looper_transaction(obj, req, HIDL_PING_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); out_data = gbinder_local_reply_data(reply); g_assert(out_data); g_assert(out_data->bytes); g_assert(out_data->bytes->len == sizeof(result)); g_assert(!memcmp(out_data->bytes->data, result, sizeof(result))); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * get_descriptor *==========================================================================*/ static void test_get_descriptor( void) { static const guint8 req_data [] = { TEST_BASE_INTERFACE_HEADER_BYTES }; int status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); GBinderLocalReply* reply; gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION, gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface)); g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface, HIDL_GET_DESCRIPTOR_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be * handled by handle_looper_transaction() */ g_assert(!gbinder_local_object_handle_transaction(obj, req, HIDL_GET_DESCRIPTOR_TRANSACTION, 0, &status)); g_assert(status == (-EBADMSG)); reply = gbinder_local_object_handle_looper_transaction(obj, req, HIDL_GET_DESCRIPTOR_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); /* Unsupported transaction */ g_assert(!gbinder_local_object_handle_looper_transaction (obj, req, -1, 0, NULL)); g_assert(!gbinder_local_object_handle_looper_transaction (obj, req, -1, 0, &status)); g_assert(status == (-EBADMSG)); g_assert(!gbinder_local_object_handle_transaction(obj, req, -1, 0, NULL)); g_assert(!gbinder_local_object_handle_transaction(obj, req, -1, 0, &status)); g_assert(status == (-EBADMSG)); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * descriptor_chain *==========================================================================*/ static void test_descriptor_chain( void) { static const guint8 req_data [] = { TEST_BASE_INTERFACE_HEADER_BYTES }; int status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL }; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); GBinderLocalReply* reply; GBinderOutputData* reply_data; gbinder_remote_request_set_data(req, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), base_interface)); g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface, HIDL_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); /* If can_handle_transaction() returns TRANSACTION_LOOPER then it must be * handled by handle_looper_transaction() */ g_assert(!gbinder_local_object_handle_transaction(obj, req, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, 0, &status)); g_assert(status == (-EBADMSG)); reply = gbinder_local_object_handle_looper_transaction(obj, req, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); /* Should get 3 buffers - vector, string and its contents */ reply_data = gbinder_local_reply_data(reply); g_assert(gbinder_output_data_offsets(reply_data)->count == 3); g_assert(gbinder_output_data_buffers_size(reply_data) == 64); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * custom_call *==========================================================================*/ #define CUSTOM_TRANSACTION (GBINDER_FIRST_CALL_TRANSACTION + 1) #define CUSTOM_INTERFACE_BYTES 'f', 'o', 'o' #define CUSTOM_INTERFACE_HEADER_BYTES CUSTOM_INTERFACE_BYTES, 0x00 static const char custom_iface[] = { CUSTOM_INTERFACE_BYTES, 0x00 }; static GBinderLocalReply* test_custom_iface_handler( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* count = user_data; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), custom_iface)); g_assert(code == CUSTOM_TRANSACTION); *status = GBINDER_STATUS_OK; (*count)++; return gbinder_local_object_new_reply(obj); } static void test_custom_iface( void) { static const guint8 req_data [] = { CUSTOM_INTERFACE_HEADER_BYTES }; const char* const ifaces[] = { custom_iface, NULL }; int count = 0, status = INT_MAX; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, test_custom_iface_handler, &count); GBinderLocalReply* reply; GBinderReaderData reader_data; GBinderReader reader; char** strv; char* str; gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION, gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(gbinder_local_object_can_handle_transaction(obj, base_interface, HIDL_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_LOOPER); g_assert(gbinder_local_object_can_handle_transaction(obj, custom_iface, HIDL_DESCRIPTOR_CHAIN_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_SUPPORTED); g_assert(gbinder_local_object_can_handle_transaction(obj, custom_iface, CUSTOM_TRANSACTION) == GBINDER_LOCAL_TRANSACTION_SUPPORTED); /* This returns the custom interface */ reply = gbinder_local_object_handle_looper_transaction(obj, req, HIDL_GET_DESCRIPTOR_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); /* Parse the reply and check the interface */ test_reader_data_init_for_reply(&reader_data, obj, reply); gbinder_reader_init(&reader, &reader_data, 0, reader_data.buffer->size); g_assert(gbinder_reader_read_int32(&reader, &status)); g_assert(status == GBINDER_STATUS_OK); str = gbinder_reader_read_hidl_string(&reader); g_assert(!g_strcmp0(str, custom_iface)); g_free(str); test_reader_data_cleanup(&reader_data); gbinder_local_reply_unref(reply); /* And this returns two interfaces */ reply = gbinder_local_object_handle_looper_transaction(obj, req, HIDL_DESCRIPTOR_CHAIN_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); /* Parse the reply and check the interface */ test_reader_data_init_for_reply(&reader_data, obj, reply); gbinder_reader_init(&reader, &reader_data, 0, reader_data.buffer->size); g_assert(gbinder_reader_read_int32(&reader, &status)); g_assert(status == GBINDER_STATUS_OK); strv = gbinder_reader_read_hidl_string_vec(&reader); g_assert(gutil_strv_length(strv) == 2); g_assert(!g_strcmp0(strv[0], custom_iface)); g_assert(!g_strcmp0(strv[1], base_interface)); g_strfreev(strv); test_reader_data_cleanup(&reader_data); gbinder_local_reply_unref(reply); /* Execute the custom transaction */ reply = gbinder_local_object_handle_transaction(obj, req, CUSTOM_TRANSACTION, 0, &status); g_assert(reply); g_assert(status == GBINDER_STATUS_OK); g_assert(count == 1); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_local_reply_unref(reply); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * reply_status *==========================================================================*/ #define EXPECTED_STATUS (424242) static GBinderLocalReply* test_reply_status_handler( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* count = user_data; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), custom_iface)); g_assert(code == CUSTOM_TRANSACTION); *status = EXPECTED_STATUS; (*count)++; return NULL; } static void test_reply_status( void) { static const guint8 req_data [] = { CUSTOM_INTERFACE_HEADER_BYTES }; const char* const ifaces[] = { custom_iface, NULL }; int count = 0, status = 0; const char* dev = GBINDER_DEFAULT_HWBINDER; const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(dev); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteRequest* req = gbinder_remote_request_new(reg, prot, 0, 0); GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, test_reply_status_handler, &count); gbinder_remote_request_set_data(req, HIDL_PING_TRANSACTION, gbinder_buffer_new(ipc->driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); /* Execute the custom transaction */ g_assert(!gbinder_local_object_handle_transaction(obj, req, CUSTOM_TRANSACTION, 0, &status)); g_assert(status == EXPECTED_STATUS); g_assert(count == 1); gbinder_ipc_unref(ipc); gbinder_local_object_unref(obj); gbinder_remote_request_unref(req); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * increfs *==========================================================================*/ static void test_increfs_cb( GBinderLocalObject* obj, void* user_data) { GVERBOSE_("%d", obj->weak_refs); g_assert(obj->weak_refs == 1); test_quit_later((GMainLoop*)user_data); } static void test_increfs_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalObject* obj = gbinder_local_object_new (ipc, NULL, NULL, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); gulong id = gbinder_local_object_add_weak_refs_changed_handler(obj, test_increfs_cb, loop); /* ipc is not an object, will be ignored */ test_binder_br_increfs(fd, ANY_THREAD, ipc); test_binder_br_increfs(fd, ANY_THREAD, obj); test_run(&test_opt, loop); g_assert(obj->weak_refs == 1); gbinder_local_object_remove_handler(obj, id); gbinder_local_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_increfs( void) { test_run_in_context(&test_opt, test_increfs_run); } /*==========================================================================* * decrefs *==========================================================================*/ static void test_decrefs_cb( GBinderLocalObject* obj, void* user_data) { GVERBOSE_("%d", obj->weak_refs); if (!obj->weak_refs) { test_quit_later((GMainLoop*)user_data); } } static void test_decrefs_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalObject* obj = gbinder_local_object_new (ipc, NULL, NULL, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); gulong id = gbinder_local_object_add_weak_refs_changed_handler(obj, test_decrefs_cb, loop); /* ipc is not an object, will be ignored */ test_binder_br_decrefs(fd, ANY_THREAD, ipc); test_binder_br_increfs(fd, ANY_THREAD, obj); test_binder_br_decrefs(fd, ANY_THREAD, obj); test_run(&test_opt, loop); g_assert(obj->weak_refs == 0); gbinder_local_object_remove_handler(obj, id); gbinder_local_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_decrefs( void) { test_run_in_context(&test_opt, test_decrefs_run); } /*==========================================================================* * acquire *==========================================================================*/ static void test_acquire_cb( GBinderLocalObject* obj, void* user_data) { GVERBOSE_("%d", obj->strong_refs); g_assert(obj->strong_refs == 1); test_quit_later((GMainLoop*)user_data); } static void test_acquire_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalObject* obj = gbinder_local_object_new (ipc, NULL, NULL, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj, test_acquire_cb, loop); /* ipc is not an object, will be ignored */ test_binder_br_acquire(fd, ANY_THREAD, ipc); test_binder_br_acquire(fd, ANY_THREAD, obj); test_run(&test_opt, loop); g_assert(obj->strong_refs == 1); gbinder_local_object_remove_handler(obj, id); gbinder_local_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_acquire( void) { test_run_in_context(&test_opt, test_acquire_run); } /*==========================================================================* * release *==========================================================================*/ static void test_release_cb( GBinderLocalObject* obj, void* user_data) { GVERBOSE_("%d", obj->strong_refs); if (!obj->strong_refs) { test_quit_later((GMainLoop*)user_data); } } static void test_release_run( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd = gbinder_driver_fd(ipc->driver); gulong id = gbinder_local_object_add_strong_refs_changed_handler(obj, test_release_cb, loop); /* ipc is not an object, will be ignored */ test_binder_br_release(fd, ANY_THREAD, ipc); test_binder_br_acquire(fd, ANY_THREAD, obj); test_binder_br_release(fd, ANY_THREAD, obj); test_run(&test_opt, loop); g_assert(obj->strong_refs == 0); gbinder_local_object_remove_handler(obj, id); gbinder_local_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_release( void) { test_run_in_context(&test_opt, test_release_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/local_object/" int main(int argc, char* argv[]) { TestConfig test_config; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "basic", test_basic); g_test_add_func(TEST_PREFIX "ping", test_ping); g_test_add_func(TEST_PREFIX "interface", test_interface); g_test_add_func(TEST_PREFIX "hidl_ping", test_hidl_ping); g_test_add_func(TEST_PREFIX "get_descriptor", test_get_descriptor); g_test_add_func(TEST_PREFIX "descriptor_chain", test_descriptor_chain); g_test_add_func(TEST_PREFIX "custom_iface", test_custom_iface); g_test_add_func(TEST_PREFIX "reply_status", test_reply_status); g_test_add_func(TEST_PREFIX "increfs", test_increfs); g_test_add_func(TEST_PREFIX "decrefs", test_decrefs); g_test_add_func(TEST_PREFIX "acquire", test_acquire); g_test_add_func(TEST_PREFIX "release", test_release); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_local_reply/000077500000000000000000000000001526127616700202435ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_local_reply/Makefile000066400000000000000000000001231526127616700216770ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_local_reply include ../common/Makefile libgbinder-1.1.54/unit/unit_local_reply/unit_local_reply.c000066400000000000000000000465441526127616700237700ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_local_object.h" #include "gbinder_local_reply_p.h" #include "gbinder_output_data.h" #include "gbinder_rpc_protocol.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_writer.h" #include "gbinder_io.h" #include "gbinder_ipc.h" #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-local-reply-XXXXXX"; static void test_int_inc( void* data) { (*((int*)data))++; } static GBinderBuffer* test_buffer_from_bytes( GBinderDriver* driver, const GByteArray* bytes) { /* Prevent double free */ test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL); return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL); } static GBinderLocalReply* test_local_reply_new() { return gbinder_local_reply_new(&gbinder_io_32, gbinder_rpc_protocol_for_device(NULL)); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderWriter writer; int count = 0; g_assert(!gbinder_local_reply_new(NULL, NULL)); g_assert(!gbinder_local_reply_new(&gbinder_io_32, NULL)); g_assert(!gbinder_local_reply_new(NULL, gbinder_rpc_protocol_for_device(NULL))); g_assert(!gbinder_local_reply_ref(NULL)); gbinder_local_reply_unref(NULL); gbinder_local_reply_init_writer(NULL, NULL); gbinder_local_reply_init_writer(NULL, &writer); g_assert(!gbinder_local_reply_data(NULL)); g_assert(!gbinder_local_reply_contents(NULL)); g_assert(!gbinder_local_reply_set_contents(NULL, NULL, NULL)); gbinder_local_reply_cleanup(NULL, NULL, &count); gbinder_local_reply_cleanup(NULL, test_int_inc, &count); g_assert(count == 1); g_assert(!gbinder_local_reply_append_bool(NULL, FALSE)); g_assert(!gbinder_local_reply_append_int32(NULL, 0)); g_assert(!gbinder_local_reply_append_int64(NULL, 0)); g_assert(!gbinder_local_reply_append_float(NULL, 0)); g_assert(!gbinder_local_reply_append_double(NULL, 0)); g_assert(!gbinder_local_reply_append_string8(NULL, NULL)); g_assert(!gbinder_local_reply_append_string16(NULL, NULL)); g_assert(!gbinder_local_reply_append_hidl_string(NULL, NULL)); g_assert(!gbinder_local_reply_append_hidl_string_vec(NULL, NULL, 0)); g_assert(!gbinder_local_reply_append_local_object(NULL, NULL)); g_assert(!gbinder_local_reply_append_remote_object(NULL, NULL)); g_assert(!gbinder_local_reply_append_fd(NULL, 0)); } /*==========================================================================* * cleanup *==========================================================================*/ static void test_cleanup( void) { GBinderLocalReply* reply = test_local_reply_new(); int count = 0; gbinder_local_reply_cleanup(reply, NULL, &count); gbinder_local_reply_cleanup(reply, test_int_inc, &count); gbinder_local_reply_cleanup(reply, test_int_inc, &count); g_assert(!count); gbinder_local_reply_unref(reply); g_assert(count == 2); } /*==========================================================================* * bool *==========================================================================*/ static void test_bool( void) { static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 }; static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 }; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_bool(reply, FALSE); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_false)); g_assert(!memcmp(data->bytes->data, output_false, data->bytes->len)); gbinder_local_reply_unref(reply); reply = test_local_reply_new(); gbinder_local_reply_append_bool(reply, TRUE); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_true)); g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len)); gbinder_local_reply_unref(reply); reply = test_local_reply_new(); gbinder_local_reply_append_bool(reply, 42); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_true)); g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * fd *==========================================================================*/ static void test_fd( void) { const gint32 fd = 1; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; GUtilIntArray* offsets; g_assert(gbinder_local_reply_append_fd(reply, fd)); data = gbinder_local_reply_data(reply); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 0); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); gbinder_local_reply_unref(reply); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { const guint32 value = 1234567; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_reply_append_int32(reply, value); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); g_assert(gbinder_local_reply_ref(reply) == reply); gbinder_local_reply_unref(reply); gbinder_local_reply_unref(reply); /* Same with writer */ reply = test_local_reply_new(); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, value); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { const guint64 value = 123456789; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_int64(reply, value); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * float *==========================================================================*/ static void test_float( void) { const gfloat value = 123456789; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_float(reply, value); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * double *==========================================================================*/ static void test_double( void) { const gdouble value = 123456789; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_double(reply, value); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * string8 *==========================================================================*/ static void test_string8( void) { /* The size of the string gets aligned at 4-byte boundary */ static const char input[] = "test"; static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_string8(reply, input); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output)); g_assert(!memcmp(data->bytes->data, output, data->bytes->len)); gbinder_local_reply_unref(reply); /* NULL string doesn't get encoded at all (should it be?) */ reply = test_local_reply_new(); gbinder_local_reply_append_string8(reply, NULL); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(!data->bytes->len); gbinder_local_reply_unref(reply); } /*==========================================================================* * string16 *==========================================================================*/ static void test_string16( void) { static const char input[] = "x"; static const guint8 output[] = { TEST_INT32_BYTES(1), TEST_INT16_BYTES('x'), 0x00, 0x00 }; const gint32 null_output = -1; GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_string16(reply, input); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output)); g_assert(!memcmp(data->bytes->data, output, data->bytes->len)); gbinder_local_reply_unref(reply); /* NULL string gets encoded as -1 */ reply = test_local_reply_new(); gbinder_local_reply_append_string16(reply, NULL); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(null_output)); g_assert(!memcmp(data->bytes->data, &null_output, data->bytes->len)); gbinder_local_reply_unref(reply); } /*==========================================================================* * hidl_string *==========================================================================*/ static void test_hidl_string( void) { GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; GUtilIntArray* offsets; gbinder_local_reply_append_hidl_string(reply, NULL); data = gbinder_local_reply_data(reply); offsets = gbinder_output_data_offsets(data); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString)); g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32); gbinder_local_reply_unref(reply); } /*==========================================================================* * hidl_string_vec *==========================================================================*/ static void test_hidl_string_vec( void) { GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; GUtilIntArray* offsets; gbinder_local_reply_append_hidl_string_vec(reply, NULL, 0); data = gbinder_local_reply_data(reply); offsets = gbinder_output_data_offsets(data); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec)); g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32); gbinder_local_reply_unref(reply); } /*==========================================================================* * local_object *==========================================================================*/ static void test_local_object( void) { GBinderLocalReply* reply; GBinderOutputData* data; GUtilIntArray* offsets; GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL); const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL }; GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); /* Append a real object (64-bit I/O is used by test_binder.c) */ reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_append_local_object(reply, obj); data = gbinder_local_reply_data(reply); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,1); g_assert_cmpuint(offsets->data[0], == ,0); g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,0); g_assert_cmpuint(data->bytes->len, == ,BINDER_OBJECT_SIZE_64); gbinder_local_reply_unref(reply); /* Append NULL object (with 32-bit I/O module) */ reply = test_local_reply_new(); gbinder_local_reply_append_local_object(reply, NULL); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,0); g_assert_cmpuint(data->bytes->len, == ,BINDER_OBJECT_SIZE_32); gbinder_local_reply_unref(reply); gbinder_ipc_unref(ipc); } /*==========================================================================* * remote_object *==========================================================================*/ static void test_remote_object( void) { GBinderLocalReply* reply = test_local_reply_new(); GBinderOutputData* data; gbinder_local_reply_append_remote_object(reply, NULL); data = gbinder_local_reply_data(reply); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); gbinder_local_reply_unref(reply); } /*==========================================================================* * remote_reply *==========================================================================*/ static void test_remote_reply( void) { /* The size of the string gets aligned at 4-byte boundary */ static const char input[] = "test"; static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); const GBinderIo* io = gbinder_driver_io(driver); GBinderLocalReply* req = gbinder_local_reply_new(io, gbinder_rpc_protocol_for_device(NULL)); GBinderLocalReply* req2; GBinderOutputData* data2; const GByteArray* bytes; const GByteArray* bytes2; GBinderBuffer* buffer; gbinder_local_reply_append_string8(req, input); bytes = gbinder_local_reply_data(req)->bytes; /* Copy flat structures (no binder objects) */ buffer = test_buffer_from_bytes(driver, bytes); req2 = gbinder_local_reply_new(io, gbinder_rpc_protocol_for_device(NULL)); g_assert(gbinder_local_reply_set_contents(req2, buffer, NULL) == req2); gbinder_buffer_free(buffer); data2 = gbinder_local_reply_data(req2); bytes2 = data2->bytes; g_assert(!gbinder_output_data_offsets(data2)); g_assert(!gbinder_output_data_buffers_size(data2)); g_assert(bytes2->len == sizeof(output)); g_assert(!memcmp(bytes2->data, output, bytes2->len)); gbinder_local_reply_unref(req2); gbinder_local_reply_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/local_reply/" int main(int argc, char* argv[]) { TestConfig test_config; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "cleanup", test_cleanup); g_test_add_func(TEST_PREFIX "bool", test_bool); g_test_add_func(TEST_PREFIX "fd", test_fd); g_test_add_func(TEST_PREFIX "int32", test_int32); g_test_add_func(TEST_PREFIX "int64", test_int64); g_test_add_func(TEST_PREFIX "float", test_float); g_test_add_func(TEST_PREFIX "double", test_double); g_test_add_func(TEST_PREFIX "string8", test_string8); g_test_add_func(TEST_PREFIX "string16", test_string16); g_test_add_func(TEST_PREFIX "hidl_string", test_hidl_string); g_test_add_func(TEST_PREFIX "hidl_string_vec", test_hidl_string_vec); g_test_add_func(TEST_PREFIX "local_object", test_local_object); g_test_add_func(TEST_PREFIX "remote_object", test_remote_object); g_test_add_func(TEST_PREFIX "remote_reply", test_remote_reply); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_local_request/000077500000000000000000000000001526127616700206005ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_local_request/Makefile000066400000000000000000000001251526127616700222360ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_local_request include ../common/Makefile libgbinder-1.1.54/unit/unit_local_request/unit_local_request.c000066400000000000000000000541571526127616700246610ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_local_object.h" #include "gbinder_local_request_p.h" #include "gbinder_output_data.h" #include "gbinder_rpc_protocol.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_writer.h" #include "gbinder_io.h" #include "gbinder_ipc.h" #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-local-request-XXXXXX"; static void test_int_inc( void* data) { (*((int*)data))++; } static GBinderBuffer* test_buffer_from_bytes( GBinderDriver* driver, const GByteArray* bytes) { /* Prevent double free */ test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL); return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL); } static GBinderBuffer* test_buffer_from_bytes_and_objects( GBinderDriver* driver, const GByteArray* bytes, void** objects) { /* Prevent double free */ test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL); return gbinder_buffer_new(driver, bytes->data, bytes->len, objects); } static GBinderLocalRequest* test_local_request_new() { return gbinder_local_request_new(&gbinder_io_32, gbinder_rpc_protocol_for_device(NULL), NULL); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderWriter writer; int count = 0; g_assert(!gbinder_local_request_new(NULL, NULL, NULL)); g_assert(!gbinder_local_request_new(&gbinder_io_32, NULL, NULL)); g_assert(!gbinder_local_request_new(NULL, gbinder_rpc_protocol_for_device(NULL), NULL)); g_assert(!gbinder_local_request_ref(NULL)); g_assert(!gbinder_local_request_new_from_data(NULL, NULL)); gbinder_local_request_unref(NULL); gbinder_local_request_init_writer(NULL, NULL); gbinder_local_request_init_writer(NULL, &writer); gbinder_local_request_cleanup(NULL, NULL, NULL); gbinder_local_request_cleanup(NULL, test_int_inc, &count); g_assert(count == 1); g_assert(!gbinder_local_request_data(NULL)); g_assert(!gbinder_local_request_append_bool(NULL, FALSE)); g_assert(!gbinder_local_request_append_int32(NULL, 0)); g_assert(!gbinder_local_request_append_int64(NULL, 0)); g_assert(!gbinder_local_request_append_float(NULL, 0)); g_assert(!gbinder_local_request_append_double(NULL, 0)); g_assert(!gbinder_local_request_append_string8(NULL, NULL)); g_assert(!gbinder_local_request_append_string16(NULL, NULL)); g_assert(!gbinder_local_request_append_hidl_string(NULL, NULL)); g_assert(!gbinder_local_request_append_hidl_string_vec(NULL, NULL, 0)); g_assert(!gbinder_local_request_append_local_object(NULL, NULL)); g_assert(!gbinder_local_request_append_remote_object(NULL, NULL)); } /*==========================================================================* * cleanup *==========================================================================*/ static void test_cleanup( void) { GBinderLocalRequest* req = test_local_request_new(); int count = 0; gbinder_local_request_cleanup(req, NULL, &count); gbinder_local_request_cleanup(req, test_int_inc, &count); gbinder_local_request_cleanup(req, test_int_inc, &count); g_assert(!count); gbinder_local_request_unref(req); g_assert(count == 2); } /*==========================================================================* * init_data *==========================================================================*/ static void test_init_data( void) { const guint8 init_data[] = { 0x01, 0x02, 0x03, 0x04 }; GBytes* init_bytes = g_bytes_new_static(init_data, sizeof(init_data)); GBinderLocalRequest* req = gbinder_local_request_new (&gbinder_io_32, gbinder_rpc_protocol_for_device(NULL), init_bytes); GBinderOutputData* data; data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(init_data)); g_assert(!memcmp(data->bytes->data, init_data, data->bytes->len)); g_assert(gbinder_local_request_ref(req) == req); gbinder_local_request_unref(req); gbinder_local_request_unref(req); req = test_local_request_new(); data = gbinder_local_request_data(req); g_assert(data->bytes); g_assert(!data->bytes->len); gbinder_local_request_unref(req); g_bytes_unref(init_bytes); } /*==========================================================================* * bool *==========================================================================*/ static void test_bool( void) { static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 }; static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_bool(req, FALSE); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_false)); g_assert(!memcmp(data->bytes->data, output_false, data->bytes->len)); gbinder_local_request_unref(req); req = test_local_request_new(); gbinder_local_request_append_bool(req, TRUE); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_true)); g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len)); gbinder_local_request_unref(req); req = test_local_request_new(); gbinder_local_request_append_bool(req, 42); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output_true)); g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { const guint32 value = 1234567; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_int32(req, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { const guint64 value = 123456789; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_int64(req, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * float *==========================================================================*/ static void test_float( void) { const gfloat value = 123456789; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_float(req, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * double *==========================================================================*/ static void test_double( void) { const gdouble value = 123456789; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_double(req, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * string8 *==========================================================================*/ static void test_string8( void) { /* The size of the string gets aligned at 4-byte boundary */ static const char input[] = "test"; static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_string8(req, input); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output)); g_assert(!memcmp(data->bytes->data, output, data->bytes->len)); gbinder_local_request_unref(req); /* NULL string doesn't get encoded at all (should it be?) */ req = test_local_request_new(); gbinder_local_request_append_string8(req, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(!data->bytes->len); gbinder_local_request_unref(req); } /*==========================================================================* * string16 *==========================================================================*/ static void test_string16( void) { static const char input[] = "x"; static const guint8 output[] = { TEST_INT32_BYTES(1), TEST_INT16_BYTES('x'), 0x00, 0x00 }; const gint32 null_output = -1; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_string16(req, input); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(output)); g_assert(!memcmp(data->bytes->data, output, data->bytes->len)); gbinder_local_request_unref(req); /* NULL string gets encoded as -1 */ req = test_local_request_new(); gbinder_local_request_append_string16(req, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(null_output)); g_assert(!memcmp(data->bytes->data, &null_output, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * hidl_string *==========================================================================*/ static void test_hidl_string( void) { GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GUtilIntArray* offsets; gbinder_local_request_append_hidl_string(req, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString)); g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32); gbinder_local_request_unref(req); } /*==========================================================================* * hidl_string_vec *==========================================================================*/ static void test_hidl_string_vec( void) { GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GUtilIntArray* offsets; gbinder_local_request_append_hidl_string_vec(req, NULL, 0); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec)); g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32); gbinder_local_request_unref(req); } /*==========================================================================* * local_object *==========================================================================*/ static void test_local_object( void) { GBinderLocalRequest* req; GBinderOutputData* data; GUtilIntArray* offsets; GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL); const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL }; GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); /* Append a real object */ req = test_local_request_new(); gbinder_local_request_append_local_object(req, obj); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,1); g_assert_cmpuint(offsets->data[0], == ,0); g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,0); g_assert_cmpuint(data->bytes->len, == ,BINDER_OBJECT_SIZE_32); gbinder_local_request_unref(req); /* Append NULL object */ req = test_local_request_new(); gbinder_local_request_append_local_object(req, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); gbinder_local_request_unref(req); gbinder_local_object_unref(obj); gbinder_ipc_unref(ipc); } /*==========================================================================* * remote_object *==========================================================================*/ static void test_remote_object( void) { GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; gbinder_local_request_append_remote_object(req, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); gbinder_local_request_unref(req); } /*==========================================================================* * remote_request *==========================================================================*/ static void test_remote_request( void) { /* The size of the string gets aligned at 4-byte boundary */ static const char input[] = "test"; static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); const GBinderIo* io = gbinder_driver_io(driver); const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver); GBinderLocalRequest* req = gbinder_local_request_new(io, protocol, NULL); GBinderLocalRequest* req2; GBinderOutputData* data2; const GByteArray* bytes; const GByteArray* bytes2; GBinderBuffer* buffer; void** no_obj = g_new0(void*, 1); gbinder_local_request_append_string8(req, input); bytes = gbinder_local_request_data(req)->bytes; /* Copy flat structures (no binder objects) */ buffer = test_buffer_from_bytes(driver, bytes); req2 = gbinder_local_request_new_from_data(buffer, NULL); gbinder_buffer_free(buffer); data2 = gbinder_local_request_data(req2); bytes2 = data2->bytes; g_assert(!gbinder_output_data_offsets(data2)); g_assert(!gbinder_output_data_buffers_size(data2)); g_assert(bytes2->len == sizeof(output)); g_assert(!memcmp(bytes2->data, output, bytes2->len)); gbinder_local_request_unref(req2); /* Same thing but with non-NULL (albeit empty) array of objects */ buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj); req2 = gbinder_local_request_new_from_data(buffer, NULL); gbinder_buffer_free(buffer); data2 = gbinder_local_request_data(req2); bytes2 = data2->bytes; g_assert(!gbinder_output_data_offsets(data2)); g_assert(!gbinder_output_data_buffers_size(data2)); g_assert(bytes2->len == sizeof(output)); g_assert(!memcmp(bytes2->data, output, bytes2->len)); gbinder_local_request_unref(req2); gbinder_local_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * remote_request_obj *==========================================================================*/ static void test_remote_request_obj_validate_data( GBinderOutputData* data) { const GByteArray* bytes = data->bytes; GUtilIntArray* offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 4); g_assert(offsets->data[1] == 4 + BUFFER_OBJECT_SIZE_64); g_assert(bytes->len == 4 + 2*BUFFER_OBJECT_SIZE_64 + BINDER_OBJECT_SIZE_64); /* GBinderHidlString + the contents (2 bytes) aligned at 8-byte boundary */ g_assert(gbinder_output_data_buffers_size(data) == (sizeof(GBinderHidlString) + 8)); } static void test_remote_request_obj( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); const GBinderIo* io = gbinder_driver_io(driver); const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver); GBinderLocalRequest* req = gbinder_local_request_new(io, protocol, NULL); GBinderLocalRequest* req2; GBinderOutputData* data; GUtilIntArray* offsets; GBinderBuffer* buffer; const GByteArray* bytes; void** objects; guint i; gbinder_local_request_append_int32(req, 1); gbinder_local_request_append_hidl_string(req, "2"); gbinder_local_request_append_local_object(req, NULL); data = gbinder_local_request_data(req); test_remote_request_obj_validate_data(data); bytes = data->bytes; offsets = gbinder_output_data_offsets(data); objects = g_new0(void*, offsets->count + 1); for (i = 0; i < offsets->count; i++) { objects[i] = bytes->data + offsets->data[i]; } buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects); req2 = gbinder_local_request_new_from_data(buffer, NULL); gbinder_buffer_free(buffer); test_remote_request_obj_validate_data(gbinder_local_request_data(req2)); /* req2 has to be freed first because req owns data */ gbinder_local_request_unref(req2); gbinder_local_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/local_request/" int main(int argc, char* argv[]) { TestConfig test_config; int result; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "cleanup", test_cleanup); g_test_add_func(TEST_PREFIX "init_data", test_init_data); g_test_add_func(TEST_PREFIX "bool", test_bool); g_test_add_func(TEST_PREFIX "int32", test_int32); g_test_add_func(TEST_PREFIX "int64", test_int64); g_test_add_func(TEST_PREFIX "float", test_float); g_test_add_func(TEST_PREFIX "double", test_double); g_test_add_func(TEST_PREFIX "string8", test_string8); g_test_add_func(TEST_PREFIX "string16", test_string16); g_test_add_func(TEST_PREFIX "hidl_string", test_hidl_string); g_test_add_func(TEST_PREFIX "hidl_string_vec", test_hidl_string_vec); g_test_add_func(TEST_PREFIX "local_object", test_local_object); g_test_add_func(TEST_PREFIX "remote_object", test_remote_object); g_test_add_func(TEST_PREFIX "remote_request", test_remote_request); g_test_add_func(TEST_PREFIX "remote_request_obj", test_remote_request_obj); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_log/000077500000000000000000000000001526127616700165175ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_log/Makefile000066400000000000000000000001131526127616700201520ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_log include ../common/Makefile libgbinder-1.1.54/unit/unit_log/unit_log.c000066400000000000000000000110711526127616700205030ustar00rootroot00000000000000/* * Copyright (C) 2020 Jolla Ltd. * Copyright (C) 2020 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_log.h" #include static TestOpt test_opt; static const char env[] = "GBINDER_DEFAULT_LOG_LEVEL"; /*==========================================================================* * empty *==========================================================================*/ static void test_empty( void) { const int level = GLOG_MODULE_NAME.level; unsetenv(env); gbinder_log_init(); g_assert_cmpint(level, == ,GLOG_MODULE_NAME.level); } /*==========================================================================* * invalid *==========================================================================*/ static void test_invalid( void) { const int level = GLOG_MODULE_NAME.level; setenv(env, "-2" /* GLOG_LEVEL_ALWAYS */, TRUE); gbinder_log_init(); g_assert_cmpint(level, == ,GLOG_MODULE_NAME.level); setenv(env, "6" /* GLOG_LEVEL_VERBOSE + 1 */, TRUE); gbinder_log_init(); g_assert_cmpint(level, == ,GLOG_MODULE_NAME.level); setenv(env, "foo", TRUE); gbinder_log_init(); g_assert_cmpint(level, == ,GLOG_MODULE_NAME.level); } /*==========================================================================* * level *==========================================================================*/ typedef struct test_level_data { const char* test_name; const char* env_value; int level; } TestLevelData; static void test_level( gconstpointer data) { const TestLevelData* test = data; GLOG_MODULE_NAME.level = GLOG_LEVEL_ALWAYS; g_assert_cmpint(GLOG_MODULE_NAME.level, != ,test->level); setenv(env, test->env_value, TRUE); gbinder_log_init(); g_assert_cmpint(GLOG_MODULE_NAME.level, == ,test->level); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/log/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { #define TEST_LEVEL_INIT(X,x) \ { TEST_(#x), #x, x } static const TestLevelData level_tests[] = { { TEST_("inherit"), "-1", GLOG_LEVEL_INHERIT }, { TEST_("none"), "0", GLOG_LEVEL_NONE }, { TEST_("err"), "1", GLOG_LEVEL_ERR }, { TEST_("warn"), "2", GLOG_LEVEL_WARN }, { TEST_("info"), "3", GLOG_LEVEL_INFO }, { TEST_("debug"), "4", GLOG_LEVEL_DEBUG }, { TEST_("verbose"), "5", GLOG_LEVEL_VERBOSE } }; guint i; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("empty"), test_empty); g_test_add_func(TEST_("invalid"), test_invalid); for (i = 0; i < G_N_ELEMENTS(level_tests); i++) { g_test_add_data_func(level_tests[i].test_name, level_tests + i, test_level); } test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_protocol/000077500000000000000000000000001526127616700175775ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_protocol/Makefile000066400000000000000000000001201526127616700212300ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_protocol include ../common/Makefile libgbinder-1.1.54/unit/unit_protocol/unit_protocol.c000066400000000000000000000347301526127616700226520ustar00rootroot00000000000000/* * Copyright (C) 2018-2023 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "gbinder_buffer_p.h" #include "gbinder_config.h" #include "gbinder_driver.h" #include "gbinder_io.h" #include "gbinder_local_request_p.h" #include "gbinder_output_data.h" #include "gbinder_reader.h" #include "gbinder_remote_request_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_writer.h" #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-protocol-XXXXXX"; #define STRICT_MODE_PENALTY_GATHER (0x40 << 16) #define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER) #define UNSET_WORK_SOURCE (-1) #define BINDER_SYS_HEADER GBINDER_FOURCC('S', 'Y', 'S', 'T') typedef struct test_data { const char* name; const char* prot; const char* dev; } TestData; typedef struct test_header_data { const char* name; const char* prot; const char* dev; const char* iface; const guint8* header; guint header_size; } TestHeaderData; static const guint8 test_header_aidl [] = { TEST_INT32_BYTES(BINDER_RPC_FLAGS), TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00, 0x00 }; static const guint8 test_header_aidl2 [] = { TEST_INT32_BYTES(BINDER_RPC_FLAGS), TEST_INT32_BYTES(UNSET_WORK_SOURCE), TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00, 0x00 }; static const guint8 test_header_aidl3 [] = { TEST_INT32_BYTES(BINDER_RPC_FLAGS), TEST_INT32_BYTES(UNSET_WORK_SOURCE), TEST_INT32_BYTES(BINDER_SYS_HEADER), TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00, 0x00 }; static const guint8 test_header_hidl [] = { 'f', 'o', 'o', 0x00 }; static const TestHeaderData test_header_tests[] = { { "aidl/ok", "aidl", GBINDER_DEFAULT_BINDER, "foo", TEST_ARRAY_AND_SIZE(test_header_aidl) }, { "aidl/short", "aidl", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl, 8 }, /* Short packet */ { "aidl2/ok", "aidl2", GBINDER_DEFAULT_BINDER, "foo", TEST_ARRAY_AND_SIZE(test_header_aidl2) }, { "aidl2/short/1", "aidl2", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl2, 1 }, /* Short packet */ { "aidl2/short/2", "aidl2", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl2, 5 }, /* Short packet */ { "aidl2/short/3", "adl2", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl2, 9 }, /* Short packet */ { "aidl3/ok", "aidl3", GBINDER_DEFAULT_BINDER, "foo", TEST_ARRAY_AND_SIZE(test_header_aidl3) }, { "aidl3/short/1", "aidl3", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl3, 1 }, /* Short packet */ { "aidl3/short/2", "aidl3", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl3, 5 }, /* Short packet */ { "aidl3/short/3", "adl3", GBINDER_DEFAULT_BINDER, NULL, test_header_aidl3, 9 }, /* Short packet */ { "hidl/ok", "hidl", GBINDER_DEFAULT_HWBINDER, "foo", TEST_ARRAY_AND_SIZE(test_header_hidl) }, { "hidl/short", "hidl", GBINDER_DEFAULT_HWBINDER, NULL, test_header_hidl, 1 } }; typedef struct test_context { TestConfig config; char* config_file; } TestContext; static void test_context_init( TestContext* test, const char* config) { memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); /* Write the config */ g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); GDEBUG("Config file %s", test->config_file); gbinder_config_file = test->config_file; } static void test_context_init2( TestContext* test, const char* dev, const char* prot) { char* config = g_strconcat("[Protocol]\n", dev, " = ", prot, "\n", NULL); test_context_init(test, config); g_free(config); } static void test_context_cleanup( TestContext* test) { remove(test->config_file); g_free(test->config_file); test_config_cleanup(&test->config); } /*==========================================================================* * device *==========================================================================*/ static void test_device( void) { const GBinderRpcProtocol* p; TestContext context; test_context_init(&context, ""); p = gbinder_rpc_protocol_for_device(NULL); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); p = gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); p = gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_HWBINDER); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); test_context_cleanup(&context); } /*==========================================================================* * config1 *==========================================================================*/ static void test_config1( void) { const GBinderRpcProtocol* p; TestContext context; test_context_init(&context, "[Protocol]\n" "/dev/binder = hidl\n" /* Redefined name for /dev/binder */ "/dev/hwbinder = foo\n"); /* Invalid protocol name */ p = gbinder_rpc_protocol_for_device(NULL); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); p = gbinder_rpc_protocol_for_device("/dev/hwbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); p = gbinder_rpc_protocol_for_device("/dev/binder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); /* Redefined by config */ p = gbinder_rpc_protocol_for_device("/dev/someotherbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); test_context_cleanup(&context); } /*==========================================================================* * config2 *==========================================================================*/ static void test_config2( void) { const GBinderRpcProtocol* p; TestContext context; test_context_init(&context, "[Protocol]\n" "Default = hidl\n" "/dev/vndbinder = hidl\n" "/dev/hwbinder = foo\n"); /* Invalid protocol name */ p = gbinder_rpc_protocol_for_device(NULL); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); p = gbinder_rpc_protocol_for_device("/dev/vndbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); p = gbinder_rpc_protocol_for_device("/dev/hwbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); p = gbinder_rpc_protocol_for_device("/dev/binder"); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); /* The default is redefined */ p = gbinder_rpc_protocol_for_device("/dev/someotherbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); test_context_cleanup(&context); } /*==========================================================================* * config3 *==========================================================================*/ static void test_config3( void) { const GBinderRpcProtocol* p; TestContext context; test_context_init(&context, "[Whatever]\n" "/dev/hwbinder = aidl\n"); /* Ignored, wrong section */ /* Just the default config */ p = gbinder_rpc_protocol_for_device(NULL); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); p = gbinder_rpc_protocol_for_device("/dev/hwbinder"); g_assert(p); g_assert_cmpstr(p->name, == ,"hidl"); p = gbinder_rpc_protocol_for_device("/dev/binder"); g_assert(p); g_assert_cmpstr(p->name, == ,"aidl"); test_context_cleanup(&context); } /*==========================================================================* * no_header1 *==========================================================================*/ static void test_no_header1( gconstpointer test_data) { const TestData* test = test_data; GBinderRemoteRequest* req; TestContext context; test_context_init2(&context, test->dev, test->prot); req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device (GBINDER_DEFAULT_BINDER), 0, 0); gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, NULL); g_assert(!gbinder_remote_request_interface(req)); gbinder_remote_request_unref(req); test_context_cleanup(&context); } /*==========================================================================* * no_header2 *==========================================================================*/ static void test_no_header2( gconstpointer test_data) { const TestData* test = test_data; const GBinderRpcProtocol* p; GBinderDriver* driver; GBinderRemoteRequest* req; TestContext context; test_context_init2(&context, test->dev, test->prot); p = gbinder_rpc_protocol_for_device(test->dev); driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, p); req = gbinder_remote_request_new(NULL, p, 0, 0); gbinder_remote_request_set_data(req, GBINDER_DUMP_TRANSACTION, gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(test_header_aidl)), sizeof(test_header_aidl), NULL)); g_assert(!gbinder_remote_request_interface(req)); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); test_context_cleanup(&context); } static const TestData test_no_header_data[] = { { "aidl", "aidl", GBINDER_DEFAULT_BINDER }, { "aidl2", "aidl2", GBINDER_DEFAULT_BINDER }, { "aidl3", "aidl3", GBINDER_DEFAULT_BINDER }, }; /*==========================================================================* * write_header *==========================================================================*/ static void test_write_header( gconstpointer test_data) { const TestHeaderData* test = test_data; const GBinderRpcProtocol* prot; GBinderLocalRequest* req; GBinderOutputData* data; GBinderWriter writer; TestContext context; test_context_init2(&context, test->dev, test->prot); prot = gbinder_rpc_protocol_for_device(test->dev); req = gbinder_local_request_new(&gbinder_io_32, gbinder_rpc_protocol_for_device(NULL), NULL); gbinder_local_request_init_writer(req, &writer); prot->write_rpc_header(&writer, test->iface); data = gbinder_local_request_data(req); g_assert(data->bytes->len == test->header_size); g_assert(!memcmp(data->bytes->data, test->header, test->header_size)); gbinder_local_request_unref(req); test_context_cleanup(&context); } /*==========================================================================* * read_header *==========================================================================*/ static void test_read_header( gconstpointer test_data) { const TestHeaderData* test = test_data; GBinderDriver* driver; GBinderRemoteRequest* req; TestContext context; test_context_init2(&context, test->dev, test->prot); driver = gbinder_driver_new(test->dev, NULL); req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device (test->dev), 0, 0); gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, g_memdup(test->header, test->header_size), test->header_size, NULL)); g_assert_cmpstr(gbinder_remote_request_interface(req), == ,test->iface); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); test_context_cleanup(&context); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/protocol/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { guint i; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("device"), test_device); g_test_add_func(TEST_("config1"), test_config1); g_test_add_func(TEST_("config2"), test_config2); g_test_add_func(TEST_("config3"), test_config3); for (i = 0; i < G_N_ELEMENTS(test_no_header_data); i++) { const TestData* test = test_no_header_data + i; char* path; path = g_strconcat(TEST_("no_header1/"), test->name, NULL); g_test_add_data_func(path, test, test_no_header1); g_free(path); path = g_strconcat(TEST_("no_header2/"), test->name, NULL); g_test_add_data_func(path, test, test_no_header2); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_header_tests); i++) { const TestHeaderData* test = test_header_tests + i; char* path; path = g_strconcat(TEST_("read_header/"), test->name, NULL); g_test_add_data_func(path, test, test_read_header); g_free(path); if (test->iface) { path = g_strconcat(TEST_("write_header/"), test->name, NULL); g_test_add_data_func(path, test, test_write_header); g_free(path); } } test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_proxy_object/000077500000000000000000000000001526127616700204455ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_proxy_object/Makefile000066400000000000000000000002261526127616700221050ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_proxy_object COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c include ../common/Makefile libgbinder-1.1.54/unit/unit_proxy_object/unit_proxy_object.c000066400000000000000000001234521526127616700243660ustar00rootroot00000000000000/* * Copyright (C) 2023-2026 Slava Monich * Copyright (C) 2021-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_ipc.h" #include "gbinder_client.h" #include "gbinder_config.h" #include "gbinder_driver.h" #include "gbinder_proxy_object.h" #include "gbinder_client_p.h" #include "gbinder_remote_object_p.h" #include "gbinder_remote_request.h" #include "gbinder_remote_reply.h" #include "gbinder_local_request.h" #include "gbinder_local_reply.h" #include "gbinder_buffer_p.h" #include "gbinder_local_request_p.h" #include "gbinder_output_data.h" #include "gbinder_object_registry.h" #include "gbinder_remote_request_p.h" #include #include #include #include static TestOpt test_opt; #define DEV "/dev/xbinder" #define DEV2 "/dev/ybinder" enum test_tx_codes { TX_CODE = GBINDER_FIRST_CALL_TRANSACTION, TX_CODE2, TX_CODE3 }; #define TX_PARAM1 0x11111111 #define TX_PARAM2 0x22222222 #define TX_PARAM3 0x33333333 #define TX_RESULT1 0x01010101 #define TX_RESULT2 0x02020202 #define TX_PARAM_REPLY 0x11110000 #define TX_PARAM_DONT_REPLY 0x22220000 #define TX_RESULT 0x03030303 static const char TMP_DIR_TEMPLATE[] = "gbinder-test-proxy_object-XXXXXX"; static const char TEST_IFACE[] = "test@1.0::ITest"; static const char TEST_IFACE2[] = "test@1.0::ITest2"; static const char* TEST_IFACES[] = { TEST_IFACE, NULL }; static const char* TEST_IFACES2[] = { TEST_IFACE2, NULL }; static const char DEFAULT_CONFIG_DATA[] = "[Protocol]\n" "Default = hidl\n" "[ServiceManager]\n" "Default = hidl\n"; /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { g_assert(!gbinder_proxy_object_new(NULL, NULL)); } /*==========================================================================* * basic *==========================================================================*/ static GBinderLocalReply* test_basic_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* count = user_data; GBinderReader reader; GDEBUG("Request handled"); g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); /* No parameters are expected */ gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_at_end(&reader)); *status = GBINDER_STATUS_OK; (*count)++; return gbinder_local_object_new_reply(obj); } static void test_basic_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* loop) { GBinderReader reader; GDEBUG("Reply received"); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); g_assert(reply); /* No parameters are expected */ gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_at_end(&reader)); g_main_loop_quit((GMainLoop*)loop); } static void test_basic_run( void) { GBinderLocalObject* obj; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderClient* client; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; GMainLoop* loop = g_main_loop_new(NULL, FALSE); int fd_obj, fd_proxy, n = 0; ipc_proxy = gbinder_ipc_new(DEV, NULL); ipc_obj = gbinder_ipc_new(DEV2, NULL); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_basic_cb, &n); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); remote_obj->stability = GBINDER_STABILITY_VINTF; g_assert(!gbinder_proxy_object_new(NULL, remote_obj)); g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); g_assert_cmpint(proxy->parent.stability, == ,GBINDER_STABILITY_VINTF); client = gbinder_client_new(proxy->remote, TEST_IFACE); /* Perform a transaction via proxy */ g_assert(gbinder_client_transact(client, TX_CODE, 0, NULL, test_basic_reply, NULL, loop)); test_run(&test_opt, loop); g_assert_cmpint(n, == ,1); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(remote_obj); gbinder_client_unref(client); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_basic( void) { test_run_in_context(&test_opt, test_basic_run); } /*==========================================================================* * empty_reply *==========================================================================*/ typedef struct test_empty_reply { GMainLoop* loop; GThread* main_thread; gboolean handled; } TestEmptyReply; static GBinderLocalReply* test_empty_reply_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestEmptyReply* test = user_data; GBinderReader reader; g_assert(test->main_thread == g_thread_self()); g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_at_end(&reader)); *status = GBINDER_STATUS_OK; test->handled = TRUE; return gbinder_local_object_new_reply(obj); } static void test_empty_reply_done( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { TestEmptyReply* test = user_data; GBinderReader reader; g_assert_cmpint(status, == ,GBINDER_STATUS_OK); g_assert(reply); gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_at_end(&reader)); g_main_loop_quit(test->loop); } static void test_empty_reply_run( void) { GBinderLocalObject* obj; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderRemoteObject* proxy_remote; GBinderClient* client; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; TestEmptyReply test; int fd_obj, fd_proxy; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); test.main_thread = g_thread_self(); ipc_proxy = gbinder_ipc_new(DEV, NULL); ipc_obj = gbinder_ipc_new(DEV2, NULL); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_empty_reply_cb, &test); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); proxy_remote = gbinder_remote_object_new(ipc_proxy, test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); client = gbinder_client_new(proxy_remote, TEST_IFACE); g_assert(gbinder_client_transact(client, TX_CODE, 0, NULL, test_empty_reply_done, NULL, &test)); test_run(&test_opt, test.loop); g_assert(test.handled); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(proxy_remote); gbinder_remote_object_unref(remote_obj); gbinder_client_unref(client); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_empty_reply( void) { test_run_in_context(&test_opt, test_empty_reply_run); } /*==========================================================================* * interface *==========================================================================*/ typedef struct test_interface { GMainLoop* loop; GBinderRemoteReply* reply; } TestInterface; static void test_interface_reply( GBinderIpc* ipc, GBinderRemoteReply* reply, int status, void* user_data) { TestInterface* test = user_data; g_assert_cmpint(status, == ,GBINDER_STATUS_OK); g_assert(reply); g_assert(!test->reply); test->reply = gbinder_remote_reply_ref(reply); g_main_loop_quit(test->loop); } static void test_interface_run( void) { GBinderLocalObject* obj; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderRemoteObject* proxy_remote; GBinderLocalRequest* req; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; char* iface; gint32 result; int fd_obj, fd_proxy; TestInterface test; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); ipc_proxy = gbinder_ipc_new(DEV, "aidl"); ipc_obj = gbinder_ipc_new(DEV2, "aidl"); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, NULL, NULL); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); proxy_remote = gbinder_remote_object_new(ipc_proxy, test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); /* * INTERFACE_TRANSACTION has no request payload. The proxy has to turn * such bufferless incoming request into an empty local request. */ req = gbinder_driver_local_request_new(ipc_proxy->driver, NULL); g_assert(gbinder_ipc_transact(ipc_proxy, proxy_remote->handle, GBINDER_INTERFACE_TRANSACTION, 0, req, test_interface_reply, NULL, &test)); gbinder_local_request_unref(req); test_run(&test_opt, test.loop); iface = gbinder_remote_reply_read_string16(test.reply); g_assert_cmpstr(iface, == ,TEST_IFACE); g_free(iface); gbinder_remote_reply_unref(test.reply); test.reply = NULL; req = gbinder_driver_local_request_new(ipc_proxy->driver, NULL); g_assert(gbinder_ipc_transact(ipc_proxy, proxy_remote->handle, GBINDER_PING_TRANSACTION, 0, req, test_interface_reply, NULL, &test)); gbinder_local_request_unref(req); test_run(&test_opt, test.loop); g_assert(gbinder_remote_reply_read_int32(test.reply, &result)); g_assert_cmpint(result, == ,GBINDER_STATUS_OK); gbinder_remote_reply_unref(test.reply); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(proxy_remote); gbinder_remote_object_unref(remote_obj); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_interface( void) { test_run_in_context(&test_opt, test_interface_run); } /*==========================================================================* * param *==========================================================================*/ typedef struct test_param_data { GMainLoop* loop; int stop; int n; } TestParamData; static gboolean test_param_cancel( gpointer req) { GDEBUG("Cancelling request"); gbinder_remote_request_complete(req, NULL, -ECANCELED); return G_SOURCE_REMOVE; } static GBinderLocalReply* test_param_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { int* count = user_data; GBinderReader reader; gint32 param = 0; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); /* Make sure that parameter got delivered intact */ gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_read_int32(&reader, ¶m)); g_assert(gbinder_reader_at_end(&reader)); *status = GBINDER_STATUS_OK; (*count)++; if (param == TX_PARAM_REPLY) { GDEBUG("Replying to request 0x%08x", param); return gbinder_local_reply_append_int32 (gbinder_local_object_new_reply(obj), TX_RESULT); } else { g_assert_cmpint(param, == ,TX_PARAM_DONT_REPLY); GDEBUG("Suspending request 0x%08x", param); gbinder_remote_request_block(req); g_timeout_add_full(G_PRIORITY_DEFAULT, 50, test_param_cancel, gbinder_remote_request_ref(req), (GDestroyNotify) gbinder_remote_request_unref); return NULL; } } static void test_param_canceled( GBinderClient* client, GBinderRemoteReply* reply, int status, void* data) { TestParamData* test = data; g_assert(!reply); g_assert_cmpint(status, == ,-ECANCELED); test->n++; GDEBUG("Transaction cancelled (%d)", test->n); if (test->n == test->stop) { g_main_loop_quit(test->loop); } } static void test_param_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* data) { TestParamData* test = data; GBinderReader reader; gint32 result = 0; g_assert(reply); g_assert_cmpint(status, == ,0); test->n++; GDEBUG("Reply received (%d)", test->n); /* Make sure that result got delivered intact */ gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(result, == ,TX_RESULT); if (test->n == test->stop) { g_main_loop_quit(test->loop); } } static void test_param_run( void) { GBinderLocalObject* obj; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderClient* client; GBinderLocalRequest* req; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; TestParamData test; int fd_obj, fd_proxy, n = 0; memset(&test, 0, sizeof(test)); test.stop = 2; test.loop = g_main_loop_new(NULL, FALSE); ipc_proxy = gbinder_ipc_new(DEV2, NULL); ipc_obj = gbinder_ipc_new(DEV, NULL); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_param_cb, &n); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); g_assert(!gbinder_proxy_object_new(NULL, remote_obj)); g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); client = gbinder_client_new(proxy->remote, TEST_IFACE); /* * Perform two transactions via proxy. First one never gets completed * and eventually is cancelled, and the second one is replied to. */ req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, TX_PARAM_DONT_REPLY); gbinder_client_transact(client, TX_CODE, 0, req, test_param_canceled, NULL, &test); gbinder_local_request_unref(req); req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, TX_PARAM_REPLY); g_assert(gbinder_client_transact(client, TX_CODE, 0, req, test_param_reply, NULL, &test)); gbinder_local_request_unref(req); test_run(&test_opt, test.loop); g_assert_cmpint(test.n, == ,2); g_assert_cmpint(n, == ,2); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(remote_obj); gbinder_client_unref(client); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_param( void) { test_run_in_context(&test_opt, test_param_run); } /*==========================================================================* * obj *==========================================================================*/ typedef struct test_obj_data { GMainLoop* loop; GBinderLocalObject* obj2; gboolean obj_call_handled; gboolean obj_call_finished; gboolean obj2_call_handled; gboolean obj2_call_finished; } TestObj; static GBinderLocalReply* test_obj2_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestObj* test = user_data; GBinderReader reader; gint32 param = 0; GDEBUG("Request 2 handled"); g_assert(!test->obj2_call_handled); test->obj2_call_handled = TRUE; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE2)); g_assert(code == TX_CODE2); /* TX_PARAM3 parameter is expected */ gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_read_int32(&reader, ¶m)); g_assert_cmpuint(param, == ,TX_PARAM3); g_assert(gbinder_reader_at_end(&reader)); *status = GBINDER_STATUS_OK; return gbinder_local_reply_append_int32 (gbinder_local_object_new_reply(obj), TX_RESULT2); } static void test_obj2_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* data) { GBinderReader reader; gint32 result = 0; TestObj* test = data; GDEBUG("Reply 2 received"); /* Make sure that result got delivered intact */ gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(result, == ,TX_RESULT2); g_assert(!test->obj2_call_finished); test->obj2_call_finished = TRUE; if (test->obj_call_finished) { GDEBUG("Both calls are done"); g_main_loop_quit(test->loop); } } static GBinderLocalReply* test_obj_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestObj* test = user_data; GBinderReader reader; GBinderRemoteObject* obj2; GBinderClient* client2; GBinderLocalRequest* req2; gint32 param = 0; GDEBUG("Request 1 handled"); g_assert(!test->obj_call_handled); test->obj_call_handled = TRUE; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); /* Read parameters: TX_PARAM1, object, TX_PARAM2 */ gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_read_int32(&reader, ¶m)); g_assert_cmpuint(param, == ,TX_PARAM1); g_assert((obj2 = gbinder_reader_read_object(&reader))); g_assert(gbinder_reader_read_int32(&reader, ¶m)); g_assert_cmpuint(param, == ,TX_PARAM2); g_assert(gbinder_reader_at_end(&reader)); /* Make sure temporary proxy won't get destroyed too early */ test->obj2 = test_binder_object(gbinder_driver_fd(obj->ipc->driver), obj2->handle); g_assert(test->obj2); /* Call remote object */ client2 = gbinder_client_new(obj2, TEST_IFACE2); req2 = gbinder_client_new_request(client2); gbinder_local_request_append_int32(req2, TX_PARAM3); gbinder_client_transact(client2, TX_CODE2, 0, req2, test_obj2_reply, NULL, test); gbinder_local_request_unref(req2); gbinder_client_unref(client2); gbinder_remote_object_unref(obj2); *status = GBINDER_STATUS_OK; return gbinder_local_reply_append_int32 (gbinder_local_object_new_reply(obj), TX_RESULT1); } static void test_obj_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* data) { GBinderReader reader; gint32 result = 0; TestObj* test = data; GDEBUG("Reply 1 received"); /* Make sure that result got delivered intact */ gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(result, == ,TX_RESULT1); g_assert(!test->obj_call_finished); test->obj_call_finished = TRUE; if (test->obj2_call_finished) { GDEBUG("Both calls are done"); g_main_loop_quit(test->loop); } } static void test_obj_run( void) { TestObj test; GBinderLocalObject* obj; GBinderLocalObject* obj2; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderClient* client; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; GBinderLocalRequest* req; int fd_obj, fd_proxy; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); ipc_proxy = gbinder_ipc_new(DEV2, NULL); ipc_obj = gbinder_ipc_new(DEV, NULL); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_obj_cb, &test); GDEBUG("obj %p", obj); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); GDEBUG("proxy %p", proxy); client = gbinder_client_new(proxy->remote, TEST_IFACE); /* Pass object reference via proxy */ obj2 = gbinder_local_object_new(ipc_obj, TEST_IFACES2, test_obj2_cb, &test); GDEBUG("obj2 %p", obj2); req = gbinder_client_new_request(client); gbinder_local_request_append_int32(req, TX_PARAM1); gbinder_local_request_append_local_object(req, obj2); gbinder_local_request_append_int32(req, TX_PARAM2); gbinder_client_transact(client, TX_CODE, 0, req, test_obj_reply, NULL, &test); gbinder_local_request_unref(req); test_run(&test_opt, test.loop); g_assert(test.obj_call_handled); g_assert(test.obj_call_finished); g_assert(test.obj2_call_handled); g_assert(test.obj2_call_finished); g_assert(test.obj2); gbinder_local_object_unref(test.obj2); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(obj2); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(remote_obj); gbinder_client_unref(client); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_obj( void) { test_run_in_context(&test_opt, test_obj_run); } /*==========================================================================* * nested_sync *==========================================================================*/ typedef struct test_nested_sync_data { GMainLoop* loop; GThread* thread; GBinderLocalObject* obj; GBinderLocalObject* connection; GBinderRemoteObject* accessor; GBinderRemoteRequest* req; gboolean register_handled; gboolean connect_handled; gboolean register_finished; } TestNestedSync; static GBinderLocalReply* test_nested_accessor_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestNestedSync* test = user_data; GBinderReader reader; GBinderRemoteObject* observer; GDEBUG("Nested accessor request handled"); g_assert(!test->connect_handled); test->connect_handled = TRUE; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE2)); g_assert(code == TX_CODE2); gbinder_remote_request_init_reader(req, &reader); g_assert((observer = gbinder_reader_read_object(&reader))); g_assert(gbinder_reader_at_end(&reader)); gbinder_remote_object_unref(observer); test->connection = gbinder_local_object_new(obj->ipc, TEST_IFACES2, NULL, NULL); g_assert(test->connection); *status = GBINDER_STATUS_OK; return gbinder_local_reply_append_int32 (gbinder_local_reply_append_local_object (gbinder_local_object_new_reply(obj), test->connection), TX_RESULT2); } static gpointer test_nested_register_thread( gpointer user_data) { TestNestedSync* test = user_data; GBinderReader reader; GBinderRemoteObject* connection; GBinderLocalObject* observer; GBinderClient* client; GBinderLocalRequest* req2; GBinderRemoteReply* reply2; GBinderLocalReply* complete; gint32 result = 0; int status2 = -1; observer = gbinder_local_object_new(test->obj->ipc, TEST_IFACES2, NULL, NULL); g_assert(observer); client = gbinder_client_new(test->accessor, TEST_IFACE2); req2 = gbinder_client_new_request(client); gbinder_local_request_append_local_object(req2, observer); reply2 = gbinder_client_transact_sync_reply2(client, TX_CODE2, req2, &status2, &gbinder_ipc_sync_worker); g_assert_cmpint(status2, == ,GBINDER_STATUS_OK); g_assert(reply2); gbinder_remote_reply_init_reader(reply2, &reader); g_assert((connection = gbinder_reader_read_object(&reader))); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert_cmpint(result, == ,TX_RESULT2); g_assert(gbinder_reader_at_end(&reader)); gbinder_remote_object_unref(connection); gbinder_remote_reply_unref(reply2); gbinder_local_request_unref(req2); gbinder_client_unref(client); gbinder_local_object_drop(observer); complete = gbinder_local_reply_append_int32 (gbinder_local_object_new_reply(test->obj), TX_RESULT1); gbinder_remote_request_complete(test->req, complete, GBINDER_STATUS_OK); gbinder_local_reply_unref(complete); return NULL; } static GBinderLocalReply* test_nested_register_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestNestedSync* test = user_data; GBinderReader reader; GDEBUG("Nested register request handled"); g_assert(!test->register_handled); test->register_handled = TRUE; g_assert(!flags); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_IFACE)); g_assert(code == TX_CODE); gbinder_remote_request_init_reader(req, &reader); g_assert((test->accessor = gbinder_reader_read_object(&reader))); /* Default system stability plus outer vendor stability requires VINTF. */ g_assert_cmpint(test->accessor->stability, == ,GBINDER_STABILITY_VINTF); g_assert(gbinder_reader_at_end(&reader)); gbinder_remote_request_block(req); test->obj = gbinder_local_object_ref(obj); test->req = gbinder_remote_request_ref(req); test->thread = g_thread_new("nested-register", test_nested_register_thread, test); *status = GBINDER_STATUS_OK; return NULL; } static void test_nested_register_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* data) { TestNestedSync* test = data; GBinderReader reader; gint32 result = 0; GDEBUG("Nested register reply received"); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); g_assert(reply); gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_read_int32(&reader, &result)); g_assert_cmpint(result, == ,TX_RESULT1); g_assert(gbinder_reader_at_end(&reader)); test->register_finished = TRUE; g_main_loop_quit(test->loop); } static void test_nested_sync_run( void) { TestNestedSync test; GBinderLocalObject* obj; GBinderLocalObject* accessor; GBinderProxyObject* proxy; GBinderRemoteObject* remote_obj; GBinderRemoteObject* proxy_remote; GBinderClient* client; GBinderLocalRequest* req; GBinderIpc* ipc_obj; GBinderIpc* ipc_proxy; int fd_obj, fd_proxy; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); ipc_proxy = gbinder_ipc_new(DEV, "aidl3"); ipc_obj = gbinder_ipc_new(DEV2, "aidl3"); fd_proxy = gbinder_driver_fd(ipc_proxy->driver); fd_obj = gbinder_driver_fd(ipc_obj->driver); obj = gbinder_local_object_new(ipc_obj, TEST_IFACES, test_nested_register_cb, &test); remote_obj = gbinder_remote_object_new(ipc_obj, test_binder_register_object(fd_obj, obj, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); remote_obj->stability = GBINDER_STABILITY_VENDOR; g_assert((proxy = gbinder_proxy_object_new(ipc_proxy, remote_obj))); g_assert_cmpint(proxy->parent.stability, == ,GBINDER_STABILITY_VENDOR); proxy_remote = gbinder_remote_object_new(ipc_proxy, test_binder_register_object(fd_proxy, &proxy->parent, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); client = gbinder_client_new(proxy_remote, TEST_IFACE); accessor = gbinder_local_object_new(ipc_proxy, TEST_IFACES2, test_nested_accessor_cb, &test); req = gbinder_client_new_request(client); gbinder_local_request_append_local_object(req, accessor); g_assert(gbinder_client_transact(client, TX_CODE, 0, req, test_nested_register_reply, NULL, &test)); gbinder_local_request_unref(req); test_run(&test_opt, test.loop); g_assert(test.register_handled); g_assert(test.connect_handled); g_assert(test.register_finished); g_thread_join(test.thread); test_binder_unregister_objects(fd_obj); test_binder_unregister_objects(fd_proxy); gbinder_local_object_drop(obj); gbinder_local_object_drop(accessor); gbinder_local_object_unref(test.obj); gbinder_local_object_drop(test.connection); gbinder_remote_object_unref(test.accessor); gbinder_remote_request_unref(test.req); gbinder_local_object_drop(&proxy->parent); gbinder_remote_object_unref(proxy_remote); gbinder_remote_object_unref(remote_obj); gbinder_client_unref(client); gbinder_ipc_unref(ipc_obj); gbinder_ipc_unref(ipc_proxy); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_nested_sync( void) { test_run_in_context(&test_opt, test_nested_sync_run); } /*==========================================================================* * return_object *==========================================================================*/ typedef struct test_return_object { GMainLoop* loop; GBinderProxyObject* proxy; GBinderBuffer* buffer; GBinderRemoteObject* original; GBinderLocalObject* forwarded; GBINDER_STABILITY_LEVEL stability; guint calls; guint replies; } TestReturnObject; static GBinderLocalReply* test_return_object_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestReturnObject* test = user_data; GBinderReader reader; GBinderRemoteObject* returned; gint32 marker; g_assert_cmpuint(code, == ,TX_CODE); g_assert(!flags); gbinder_remote_request_init_reader(req, &reader); g_assert((returned = gbinder_reader_read_object(&reader))); g_assert(returned->ipc == test->original->ipc); g_assert_cmpuint(returned->handle, == ,test->original->handle); g_assert_cmpint(returned->stability, == ,test->stability); gbinder_remote_object_unref(returned); g_assert(gbinder_reader_read_int32(&reader, &marker)); g_assert_cmpint(marker, == ,TX_PARAM1); g_assert(gbinder_reader_at_end(&reader)); test->calls++; *status = GBINDER_STATUS_OK; return gbinder_local_object_new_reply(obj); } static GBinderLocalReply* test_forward_object_cb( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestReturnObject* test = user_data; GBinderReader reader; GBinderRemoteObject* forwarded; GBinderProxyObject* proxy; gbinder_remote_request_init_reader(req, &reader); g_assert((forwarded = gbinder_reader_read_object(&reader))); g_assert_cmpint(forwarded->stability, == ,GBINDER_STABILITY_VINTF); g_assert(gbinder_reader_at_end(&reader)); test->forwarded = test_binder_object(gbinder_driver_fd(obj->ipc->driver), forwarded->handle); g_assert(test->forwarded); proxy = GBINDER_PROXY_OBJECT(test->forwarded); g_assert(proxy->remote == test->original); g_assert_cmpint(proxy->remote->stability, == ,test->stability); gbinder_remote_object_unref(forwarded); *status = GBINDER_STATUS_OK; return gbinder_local_object_new_reply(obj); } static void test_return_object_reply( GBinderClient* client, GBinderRemoteReply* reply, int status, void* user_data) { TestReturnObject* test = user_data; g_assert_cmpint(status, == ,GBINDER_STATUS_OK); g_assert(reply); test->replies++; g_main_loop_quit(test->loop); } static GBinderLocalReply* test_return_object_forward( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { TestReturnObject* test = user_data; /* Keep the live transaction so the proxy can complete asynchronously, * replacing only the payload with the returning local Binder object. */ g_assert(req->tx); g_assert(test->buffer); gbinder_remote_request_set_data(req, code, test->buffer); test->buffer = NULL; return gbinder_local_object_handle_transaction(&test->proxy->parent, req, code, flags, status); } static void test_return_object_run( gconstpointer param) { GBinderIpc* src = gbinder_ipc_new(DEV, "aidl3"); GBinderIpc* dest = gbinder_ipc_new(DEV2, "aidl3"); int src_fd = gbinder_driver_fd(src->driver); int dest_fd = gbinder_driver_fd(dest->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* original = gbinder_local_object_new(dest, TEST_IFACES2, NULL, NULL); GBinderRemoteObject* remote = gbinder_object_registry_get_remote (gbinder_ipc_object_registry(dest), test_binder_register_object(dest_fd, original, 11), REMOTE_REGISTRY_CAN_CREATE); TestReturnObject test; GBinderLocalObject* receiver; GBinderRemoteObject* receiver_remote; GBinderProxyObject* receiver_proxy; GBinderLocalObject* return_sender; GBinderLocalObject* forward_receiver; GBinderRemoteObject* forward_remote; GBinderProxyObject* forward_proxy; GBinderRemoteObject* client_remote; GBinderClient* client; GBinderLocalRequest* local_req; GBinderOutputData* output; GUtilIntArray* offsets; guint8* bytes; void** objects; memset(&test, 0, sizeof(test)); test.loop = loop; test.original = remote; test.stability = original->stability = GPOINTER_TO_INT(param); g_assert_cmpint(remote->stability, == ,GBINDER_STABILITY_SYSTEM); /* Forward the original object through a VINTF service. The converter * must retain its wire stability even when the proxy is promoted. */ forward_receiver = gbinder_local_object_new(src, TEST_IFACES, test_forward_object_cb, &test); forward_remote = gbinder_remote_object_new(src, test_binder_register_object(src_fd, forward_receiver, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); forward_remote->stability = GBINDER_STABILITY_VINTF; forward_proxy = gbinder_proxy_object_new(dest, forward_remote); client_remote = gbinder_remote_object_new(dest, test_binder_register_object(dest_fd, &forward_proxy->parent, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); client = gbinder_client_new(client_remote, TEST_IFACE); local_req = gbinder_client_new_request(client); gbinder_local_request_append_local_object(local_req, original); /* The mock may deliver to a looper, which needs the main context. */ g_assert(gbinder_client_transact(client, TX_CODE, 0, local_req, test_return_object_reply, NULL, &test)); gbinder_local_request_unref(local_req); test_run(&test_opt, loop); g_assert_cmpuint(test.replies, == ,1); g_assert(test.forwarded); gbinder_client_unref(client); gbinder_remote_object_unref(client_remote); receiver = gbinder_local_object_new(dest, TEST_IFACES, test_return_object_cb, &test); receiver_remote = gbinder_remote_object_new(dest, test_binder_register_object(dest_fd, receiver, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); test.proxy = receiver_proxy = gbinder_proxy_object_new(src, receiver_remote); /* Emulate the kernel returning a proxy as BINDER_TYPE_BINDER to its * owner. The fake driver normally only converts BINDER to HANDLE. */ local_req = gbinder_local_request_new_iface(gbinder_ipc_io(src), gbinder_ipc_protocol(src), TEST_IFACE); gbinder_local_request_append_local_object(local_req, test.forwarded); gbinder_local_request_append_int32(local_req, TX_PARAM1); output = gbinder_local_request_data(local_req); offsets = gbinder_output_data_offsets(output); bytes = gutil_memdup(output->bytes->data, output->bytes->len); g_assert_cmpuint(offsets->count, == ,1); objects = g_new0(void*, 2); objects[0] = bytes + offsets->data[0]; test.buffer = gbinder_buffer_new(src->driver, bytes, output->bytes->len, objects); gbinder_local_request_unref(local_req); /* Inject the return payload into an asynchronous transaction. The * receiver must get the original handle, not another proxy node. */ return_sender = gbinder_local_object_new(src, TEST_IFACES, test_return_object_forward, &test); client_remote = gbinder_remote_object_new(src, test_binder_register_object(src_fd, return_sender, AUTO_HANDLE), REMOTE_OBJECT_CREATE_ALIVE); client = gbinder_client_new(client_remote, TEST_IFACE); g_assert(gbinder_client_transact(client, TX_CODE, 0, NULL, test_return_object_reply, NULL, &test)); test_run(&test_opt, loop); g_assert_cmpuint(test.replies, == ,2); g_assert_cmpuint(test.calls, == ,1); g_assert(!test.buffer); test_binder_unregister_objects(src_fd); test_binder_unregister_objects(dest_fd); gbinder_local_object_drop(return_sender); gbinder_local_object_drop(&receiver_proxy->parent); gbinder_local_object_drop(test.forwarded); gbinder_local_object_drop(&forward_proxy->parent); gbinder_local_object_drop(forward_receiver); gbinder_remote_object_unref(forward_remote); gbinder_remote_object_unref(client_remote); gbinder_client_unref(client); gbinder_local_object_drop(receiver); gbinder_local_object_drop(original); gbinder_remote_object_unref(receiver_remote); gbinder_remote_object_unref(remote); gbinder_ipc_unref(src); gbinder_ipc_unref(dest); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_return_object( gconstpointer param) { test_run_in_context_param(&test_opt, test_return_object_run, param); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/proxy_object/" t int main(int argc, char* argv[]) { TestConfig test_config; char* config_file; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("empty_reply"), test_empty_reply); g_test_add_func(TEST_("interface"), test_interface); g_test_add_func(TEST_("param"), test_param); g_test_add_func(TEST_("obj"), test_obj); g_test_add_func(TEST_("nested_sync"), test_nested_sync); g_test_add_data_func(TEST_("return_object/system"), GINT_TO_POINTER(GBINDER_STABILITY_SYSTEM), test_return_object); g_test_add_data_func(TEST_("return_object/vendor"), GINT_TO_POINTER(GBINDER_STABILITY_VENDOR), test_return_object); g_test_add_data_func(TEST_("return_object/vintf"), GINT_TO_POINTER(GBINDER_STABILITY_VINTF), test_return_object); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); config_file = g_build_filename(test_config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(config_file, DEFAULT_CONFIG_DATA, -1, NULL)); GDEBUG("Config file %s", config_file); gbinder_config_file = config_file; result = g_test_run(); remove(config_file); g_free(config_file); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_reader/000077500000000000000000000000001526127616700172005ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_reader/Makefile000066400000000000000000000001161526127616700206360ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_reader include ../common/Makefile libgbinder-1.1.54/unit/unit_reader/unit_reader.c000066400000000000000000003036551526127616700216610ustar00rootroot00000000000000/* * Copyright (C) 2026 Jolla Mobile Ltd * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_reader_p.h" #include "gbinder_remote_object_p.h" #include "gbinder_io.h" #include #include #include static TestOpt test_opt; typedef struct binder_buffer_object_64 { guint32 type; guint32 flags; union { const void* ptr; guint64 value; } buffer; guint64 length; guint64 parent; guint64 parent_offset; } BinderObject64; #define BINDER_TYPE_(c1,c2,c3) GBINDER_FOURCC(c1,c2,c3,0x85) #define BINDER_TYPE_FD BINDER_TYPE_('f', 'd', '*') #define BINDER_BUFFER_FLAG_HAS_PARENT 0x01 #define BINDER_FLAG_ACCEPTS_FDS 0x100 G_STATIC_ASSERT(sizeof(BinderObject64) == BUFFER_OBJECT_SIZE_64); #define TEST_BINDER_OBJECT_64(handle) \ TEST_INT32_BYTES(BINDER_TYPE_HANDLE), TEST_INT32_BYTES(0 /* flags */), \ TEST_INT32_BYTES(handle), TEST_INT32_BYTES(0 /* padding */), \ TEST_INT64_BYTES(0 /* cookie */ ) static void test_init_reader( GBinderDriver* driver, GBinderReaderData* data, GBinderReader* reader, const void* bytes, gsize size) { data->buffer = gbinder_buffer_new(driver, gutil_memdup(bytes, size), size, NULL); gbinder_reader_init(reader, data, 0, size); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderReader reader; /* NULL const pointers are tolerated */ g_assert(gbinder_reader_at_end(NULL)); g_assert(!gbinder_reader_bytes_read(NULL)); g_assert(!gbinder_reader_bytes_remaining(NULL)); g_assert(!gbinder_reader_get_data(NULL, NULL)); gbinder_reader_copy(&reader, NULL); g_assert(gbinder_reader_at_end(&reader)); } /*==========================================================================* * empty *==========================================================================*/ static void test_empty( void) { GBinderReader reader, parcelable; gsize count = 1, elemsize = 1; gsize size = 1; gboolean non_null; gbinder_reader_init(&reader, NULL, 0, 0); g_assert(gbinder_reader_at_end(&reader)); g_assert(!gbinder_reader_get_data(&reader, NULL)); g_assert(!gbinder_reader_get_data(&reader, &size)); g_assert(!size); g_assert(!gbinder_reader_bytes_read(&reader)); g_assert(!gbinder_reader_bytes_remaining(&reader)); g_assert(!gbinder_reader_read_byte(&reader, NULL)); g_assert(!gbinder_reader_read_bool(&reader, NULL)); g_assert(!gbinder_reader_read_int8(&reader, NULL)); g_assert(!gbinder_reader_read_int16(&reader, NULL)); g_assert(!gbinder_reader_read_int32(&reader, NULL)); g_assert(!gbinder_reader_read_uint32(&reader, NULL)); g_assert(!gbinder_reader_read_int64(&reader, NULL)); g_assert(!gbinder_reader_read_uint64(&reader, NULL)); g_assert(!gbinder_reader_read_float(&reader, NULL)); g_assert(!gbinder_reader_read_double(&reader, NULL)); g_assert(!gbinder_reader_read_object(&reader)); g_assert(!gbinder_reader_read_nullable_object(&reader, NULL)); g_assert(!gbinder_reader_read_buffer(&reader)); g_assert(!gbinder_reader_read_parcelable(&reader, NULL)); g_assert(!gbinder_reader_read_hidl_struct1(&reader, 1)); g_assert(!gbinder_reader_read_hidl_vec(&reader, NULL, NULL)); g_assert(!gbinder_reader_read_hidl_vec(&reader, &count, &elemsize)); g_assert(!gbinder_reader_read_hidl_vec1(&reader, NULL, 1)); g_assert(!gbinder_reader_read_hidl_vec1(&reader, &count, 1)); g_assert(!count); g_assert(!elemsize); g_assert(!gbinder_reader_skip_hidl_string(&reader)); g_assert(!gbinder_reader_read_hidl_string(&reader)); g_assert(!gbinder_reader_read_hidl_string_vec(&reader)); g_assert(!gbinder_reader_skip_buffer(&reader)); g_assert(!gbinder_reader_read_string8(&reader)); g_assert(!gbinder_reader_read_string16(&reader)); g_assert(!gbinder_reader_skip_string16(&reader)); g_assert(!gbinder_reader_read_byte_array(&reader, &size)); /* * gbinder_reader_start_parcelable() fails but still initializes * the parcelable reading, which isn't suitable for passing to * gbinder_reader_finish_parcelable, but it's handled more or less * gracefully (with a warning being printed to the console) */ g_assert(!gbinder_reader_start_parcelable(&reader, &parcelable, NULL)); g_assert(!gbinder_reader_start_parcelable(&reader, &parcelable, &non_null)); g_assert_false(non_null); gbinder_reader_finish_parcelable(&parcelable); g_assert_false(gbinder_reader_skip_parcelable(&reader)); } /*==========================================================================* * byte *==========================================================================*/ static void test_byte( void) { const guint8 in = 42; guint8 out = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_byte(&reader, &out)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpuint(in, == ,out); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_byte(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * bool *==========================================================================*/ static void test_bool( void) { const guint8 in_short[] = { 0 }; const guint8 in_true[] = { TEST_INT8_BYTES_4(TRUE) }; const guint8 in_false[] = { TEST_INT8_BYTES_4(FALSE) }; gboolean out = FALSE; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); /* not enough data */ data.buffer = gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(in_short)), sizeof(in_short), NULL); gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(!gbinder_reader_read_bool(&reader, NULL)); g_assert(!gbinder_reader_read_bool(&reader, &out)); g_assert(!gbinder_reader_at_end(&reader)); /* true */ gbinder_buffer_free(data.buffer); data.buffer = gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(in_true)), sizeof(in_true), NULL); gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(gbinder_reader_read_bool(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(gbinder_reader_read_bool(&reader, &out)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpuint(out, == ,TRUE); /* false */ gbinder_buffer_free(data.buffer); data.buffer = gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(in_false)), sizeof(in_false), NULL); gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(gbinder_reader_read_bool(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(gbinder_reader_read_bool(&reader, &out)); g_assert(gbinder_reader_at_end(&reader)); g_assert(out == FALSE); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * int8 *==========================================================================*/ static void test_int8( void) { const guint8 in = 0x2a; const guint8 in4[] = { 0x2a, 0x00, 0x00, 0x00 }; guint8 out1 = 0; gint8 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); /* Not enough data */ data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(!gbinder_reader_read_uint8(&reader, &out1)); g_assert(!gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); /* Enough data */ data.buffer = gbinder_buffer_new(driver, g_memdup(in4, sizeof(in4)), sizeof(in4), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_uint8(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpuint(in, == ,out1); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_int8(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(in, == ,out2); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_int8(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * int16 *==========================================================================*/ static void test_int16( void) { const guint16 in = 42; const guint8 in4[] = { TEST_INT16_BYTES(42), 0x00, 0x00 }; guint16 out1 = 0; gint16 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); /* Not enough data */ data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(!gbinder_reader_read_uint16(&reader, &out1)); g_assert(!gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); /* Enough data */ data.buffer = gbinder_buffer_new(driver, g_memdup(in4, sizeof(in4)), sizeof(in4), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_uint16(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpuint(in, == ,out1); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_int16(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpint(in, == ,out2); gbinder_reader_init(&reader, &data, 0, sizeof(in4)); g_assert(gbinder_reader_read_int16(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { const guint32 in = 42; guint32 out1 = 0; gint32 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_uint32(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == out1); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_int32(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == (guint32)out2); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_int32(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { const guint64 in = 42; guint64 out1 = 0; gint64 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_uint64(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == out1); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_int64(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == (guint64)out2); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_int64(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * float *==========================================================================*/ static void test_float( void) { const gfloat in = 42; gfloat out1 = 0; gfloat out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_float(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == out1); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_float(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == (gfloat)out2); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_float(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * double *==========================================================================*/ static void test_double( void) { const gdouble in = 42; gdouble out1 = 0; gdouble out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(&in, sizeof(in)), sizeof(in), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_double(&reader, &out1)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == out1); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_double(&reader, &out2)); g_assert(gbinder_reader_at_end(&reader)); g_assert(in == (gdouble)out2); gbinder_reader_init(&reader, &data, 0, sizeof(in)); g_assert(gbinder_reader_read_double(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * cstring *==========================================================================*/ typedef struct test_string_data { const char* name; const guint8* in; guint in_size; const char* out; gboolean remaining; } TestStringData; static const guint8 test_cstring_in_short [] = { 't', 'e', 's', 't', 0, 0, 0 }; static const guint8 test_cstring_in_basic1 [] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; static const guint8 test_cstring_in_basic2 [] = { 't', 'e', 's', 't', 0, 0, 0, 0, 0 }; static const TestStringData test_cstring_tests [] = { { "err", TEST_ARRAY_AND_SIZE(test_cstring_in_short), NULL, sizeof(test_cstring_in_short)}, { "ok1", TEST_ARRAY_AND_SIZE(test_cstring_in_basic1), "test", 0 }, { "ok2", TEST_ARRAY_AND_SIZE(test_cstring_in_basic2), "test", 1 } }; static void test_cstring( gconstpointer test_data) { const TestStringData* test = test_data; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; const char* str; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(test->in, test->in_size), test->in_size, NULL); gbinder_reader_init(&reader, &data, 0, test->in_size); str = gbinder_reader_read_string8(&reader); g_assert(!g_strcmp0(str, test->out)); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * string8 *==========================================================================*/ static const guint8 test_string8_in_null [] = { TEST_INT32_BYTES(-1) }; static const guint8 test_string8_in_invalid [] = { TEST_INT32_BYTES(-2) }; static const guint8 test_string8_in_too_long [] = { TEST_INT32_BYTES(INT32_MAX) }; static const guint8 test_string8_in_short1 [] = { 0x00 }; static const guint8 test_string8_in_short2 [] = { TEST_INT32_BYTES(3), 'f', 'o', 'o' }; static const guint8 test_string8_in_noterm [] = { TEST_INT32_BYTES(3), 'f', 'o', 'o', 'x' /* Missing terminator */ }; static const guint8 test_string8_in_basic1 [] = { TEST_INT32_BYTES(3), 'f', 'o', 'o', 0x00 }; static const guint8 test_string8_in_basic2 [] = { TEST_INT32_BYTES(3), 'f', 'o', 'o', 0x00, 0x00 }; static const TestStringData test_string8_tests [] = { { "invalid", TEST_ARRAY_AND_SIZE(test_string8_in_invalid), NULL, sizeof(test_string8_in_invalid) }, { "too_long", TEST_ARRAY_AND_SIZE(test_string8_in_too_long), NULL, sizeof(test_string8_in_too_long) }, { "short1", TEST_ARRAY_AND_SIZE(test_string8_in_short1), NULL, sizeof(test_string8_in_short1) }, { "short2", TEST_ARRAY_AND_SIZE(test_string8_in_short2), NULL, sizeof(test_string8_in_short2) }, { "noterm", TEST_ARRAY_AND_SIZE(test_string8_in_noterm), NULL, sizeof(test_string8_in_noterm) }, { "ok1", TEST_ARRAY_AND_SIZE(test_string8_in_basic1), "foo", 0 }, { "ok2", TEST_ARRAY_AND_SIZE(test_string8_in_basic2), "foo", 1 } }; static void test_string8_null( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; gsize len = 1; char dummy; const char* out = &dummy; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(test_string8_in_null)), sizeof(test_string8_in_null), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(test_string8_in_null)); g_assert(gbinder_reader_skip_nullable_string8(&reader)); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, sizeof(test_string8_in_null)); g_assert(gbinder_reader_read_nullable_string8(&reader, &out, &len)); g_assert(gbinder_reader_at_end(&reader)); g_assert(!out); g_assert(!len); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } static void test_string8( gconstpointer test_data) { const TestStringData* test = test_data; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader r; GBinderReaderData data; const gboolean valid = (test->out != NULL); const char* out = NULL; gsize len = 0; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(test->in, test->in_size), test->in_size, NULL); gbinder_reader_init(&r, &data, 0, test->in_size); g_assert(gbinder_reader_skip_nullable_string8(&r) == valid); g_assert(gbinder_reader_at_end(&r) == (!test->remaining)); g_assert_cmpuint(gbinder_reader_bytes_remaining(&r), == ,test->remaining); gbinder_reader_init(&r, &data, 0, test->in_size); g_assert(gbinder_reader_read_nullable_string8(&r, &out, &len) == valid); g_assert(gbinder_reader_at_end(&r) == (!test->remaining)); g_assert_cmpuint(gbinder_reader_bytes_remaining(&r), == ,test->remaining); if (valid) { g_assert_cmpstr(out, ==, test->out); g_assert_cmpuint(len, == ,strlen(test->out)); } gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * string16 *==========================================================================*/ static const guint8 test_string16_in_null [] = { TEST_INT32_BYTES(-1) }; static const guint8 test_string16_in_invalid [] = { TEST_INT32_BYTES(-2) }; static const guint8 test_string16_in_too_long [] = { TEST_INT32_BYTES(INT32_MAX) }; static const guint8 test_string16_in_short [] = { TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00 }; static const guint8 test_string16_in_noterm [] = { TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o') /* Missing terminator */ }; static const guint8 test_string16_in_basic1 [] = { TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00, 0x00 }; static const guint8 test_string16_in_basic2 [] = { TEST_INT32_BYTES(3), TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), TEST_INT16_BYTES('o'), 0x00, 0x00, 0x00 }; static const TestStringData test_string16_tests [] = { { "invalid", TEST_ARRAY_AND_SIZE(test_string16_in_invalid), NULL, sizeof(test_string16_in_invalid) }, { "too_long", TEST_ARRAY_AND_SIZE(test_string16_in_too_long), NULL, sizeof(test_string16_in_too_long) }, { "short", TEST_ARRAY_AND_SIZE(test_string16_in_short), NULL, sizeof(test_string16_in_short) }, { "noterm", TEST_ARRAY_AND_SIZE(test_string16_in_noterm), NULL, sizeof(test_string16_in_noterm) }, { "ok1", TEST_ARRAY_AND_SIZE(test_string16_in_basic1), "foo", 0 }, { "ok2", TEST_ARRAY_AND_SIZE(test_string16_in_basic2), "foo", 1 } }; static void test_string16_null( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; const gunichar2* out2 = NULL; gsize len = 1; char dummy; char* out = &dummy; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(TEST_ARRAY_AND_SIZE(test_string16_in_null)), sizeof(test_string16_in_null), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, NULL, NULL)); g_assert(gbinder_reader_at_end(&reader)); len = 1; gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, &out2, &len)); g_assert(gbinder_reader_at_end(&reader)); g_assert(!out2); g_assert(!len); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(!gbinder_reader_read_string16_utf16(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); len = 1; gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(!gbinder_reader_read_string16_utf16(&reader, &len)); g_assert(gbinder_reader_at_end(&reader)); g_assert(!len); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(gbinder_reader_read_nullable_string16(&reader, NULL)); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(gbinder_reader_read_nullable_string16(&reader, &out)); g_assert(!out); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(!gbinder_reader_read_string16(&reader)); g_assert(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, sizeof(test_string16_in_null)); g_assert(gbinder_reader_skip_string16(&reader)); g_assert(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } static void test_string16( gconstpointer test_data) { const TestStringData* test = test_data; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader; GBinderReaderData data; const gboolean valid = (test->out != NULL); const gunichar2* out2 = NULL; gsize len = 0; char* str = NULL; g_assert(driver); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, g_memdup(test->in, test->in_size), test->in_size, NULL); gbinder_reader_init(&reader, &data, 0, test->in_size); if (valid) { out2 = gbinder_reader_read_string16_utf16(&reader, &len); g_assert(out2); g_assert((gsize)len == strlen(test->out)); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); } else { g_assert(!gbinder_reader_read_string16_utf16(&reader, NULL)); } g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, NULL, NULL) == valid); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_nullable_string16_utf16(&reader, &out2, &len) == valid); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); if (valid) { g_assert(out2); g_assert((gsize)len == strlen(test->out)); } gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_nullable_string16(&reader, NULL) == valid); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_nullable_string16(&reader, &str) == valid); g_assert(!g_strcmp0(str, test->out)); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); g_free(str); gbinder_reader_init(&reader, &data, 0, test->in_size); str = gbinder_reader_read_string16(&reader); g_assert(!g_strcmp0(str, test->out)); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); g_free(str); gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_skip_string16(&reader) == (test->out != NULL)); g_assert(gbinder_reader_at_end(&reader) == (!test->remaining)); g_assert(gbinder_reader_bytes_remaining(&reader) == test->remaining); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * hidl_struct *==========================================================================*/ typedef struct test_hidl_struct { const char* name; const void* in; guint in_size; guint struct_size; const void* data; } TestHidlStruct; typedef struct test_hidl_struct_type { guint32 x; } TestHidlStructType; static const TestHidlStructType test_hidl_struct_data = { 0 }; static const BinderObject64 test_hidl_struct_ok_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_struct_data }, sizeof(test_hidl_struct_data), 0, 0 } }; static const BinderObject64 test_hidl_struct_big_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_struct_data }, 2 * sizeof(test_hidl_struct_data), 0, 0 } }; static const TestHidlStruct test_hidl_struct_tests[] = { { "ok", TEST_ARRAY_AND_SIZE(test_hidl_struct_ok_buf), sizeof(TestHidlStructType), &test_hidl_struct_data }, { "badsize", TEST_ARRAY_AND_SIZE(test_hidl_struct_big_buf), sizeof(TestHidlStructType), NULL } }; static void test_hidl_struct( gconstpointer test_data) { const TestHidlStruct* test = test_data; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(test->in, test->in_size), test->in_size, NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new0(void*, 2); data.objects[0] = buf->data; gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_hidl_struct1(&reader, test->struct_size) == test->data); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_vec *==========================================================================*/ typedef struct test_hidl_vec { const char* name; const void* in; guint in_size; const guint* offset; guint offset_count; const void* data; guint count; guint elemsize; } TestHidlVec; static const guint test_hidl_vec_2offsets [] = { 0, BUFFER_OBJECT_SIZE_64 }; static const guint8 test_hidl_vec_2bytes_data [] = { 0x01, 0x02 }; static const GBinderHidlVec test_hidl_vec_2bytes = { .data.ptr = test_hidl_vec_2bytes_data, sizeof(test_hidl_vec_2bytes_data), TRUE }; static const BinderObject64 test_hidl_vec_2bytes_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_2bytes }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_2bytes_data }, sizeof(test_hidl_vec_2bytes_data), 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; static const GBinderHidlVec test_hidl_vec_empty = { .data.ptr = test_hidl_vec_2bytes_data, 0, TRUE }; static const BinderObject64 test_hidl_vec_empty_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_empty }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_2bytes_data }, 0, 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; static const guint test_hidl_vec_1offset [] = {0}; static const GBinderHidlVec test_hidl_vec_null = {{0}, 0, TRUE}; static const BinderObject64 test_hidl_vec_null_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_null }, sizeof(GBinderHidlVec), 0, 0 } }; /* Buffer smaller than GBinderHidlVec */ static const BinderObject64 test_hidl_vec_short_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_empty }, sizeof(GBinderHidlVec) - 1, 0, 0 } }; /* NULL buffer with size 1 */ static const GBinderHidlVec test_hidl_vec_badnull = {{0}, 1, TRUE}; static const BinderObject64 test_hidl_vec_badnull_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_badnull }, sizeof(GBinderHidlVec), 0, 0 } }; /* Buffer size not divisible by count */ static const guint8 test_hidl_vec_badsize_data [] = { 0x01, 0x02, 0x03 }; static const GBinderHidlVec test_hidl_vec_badsize = { .data.ptr = test_hidl_vec_badsize_data, 2, TRUE }; static const BinderObject64 test_hidl_vec_badsize_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_badsize }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_badsize_data }, sizeof(test_hidl_vec_badsize_data), 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; /* Bad buffer address */ static const guint8 test_hidl_vec_badbuf_data [] = { 0x01, 0x02, 0x03 }; static const GBinderHidlVec test_hidl_vec_badbuf = { .data.ptr = test_hidl_vec_badbuf_data, sizeof(test_hidl_vec_badbuf_data), TRUE }; static const BinderObject64 test_hidl_vec_badbuf_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_badbuf }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_badsize_data }, sizeof(test_hidl_vec_badsize_data), 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; /* Non-zero count and zero size */ static const GBinderHidlVec test_hidl_vec_badcount1 = { .data.ptr = test_hidl_vec_badsize_data, 1, TRUE }; static const BinderObject64 test_hidl_vec_badcount1_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_badcount1 }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_badsize_data }, 0, 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; /* Zero count0 and non-zero size */ static const GBinderHidlVec test_hidl_vec_badcount2 = { .data.ptr = test_hidl_vec_badsize_data, 0, TRUE }; static const BinderObject64 test_hidl_vec_badcount2_buf [] = { { BINDER_TYPE_PTR, 0, { &test_hidl_vec_badcount2 }, sizeof(GBinderHidlVec), 0, 0 },{ BINDER_TYPE_PTR, BINDER_BUFFER_FLAG_HAS_PARENT, { test_hidl_vec_badsize_data }, sizeof(test_hidl_vec_badsize_data), 0, GBINDER_HIDL_VEC_BUFFER_OFFSET } }; static const TestHidlVec test_hidl_vec_tests[] = { { "2bytes", TEST_ARRAY_AND_SIZE(test_hidl_vec_2bytes_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), TEST_ARRAY_AND_SIZE(test_hidl_vec_2bytes_data), 1 }, { "empty", TEST_ARRAY_AND_SIZE(test_hidl_vec_empty_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), test_hidl_vec_2bytes_data, 0, 0 }, { "null", TEST_ARRAY_AND_SIZE(test_hidl_vec_null_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_1offset), &test_hidl_vec_null, 0, 0 }, { "missingbuf", test_hidl_vec_2bytes_buf, sizeof(test_hidl_vec_2bytes_buf[0]), TEST_ARRAY_AND_SIZE(test_hidl_vec_1offset), NULL, 0, 0 }, { "shortbuf", TEST_ARRAY_AND_SIZE(test_hidl_vec_short_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_1offset), NULL, 0, 0 }, { "badnull", TEST_ARRAY_AND_SIZE(test_hidl_vec_badnull_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_1offset), NULL, 0, 0 }, { "badsize", TEST_ARRAY_AND_SIZE(test_hidl_vec_badsize_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), NULL, 0, 0 }, { "badbuf", TEST_ARRAY_AND_SIZE(test_hidl_vec_badbuf_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), NULL, 0, 0 }, { "badcount1", TEST_ARRAY_AND_SIZE(test_hidl_vec_badcount1_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), NULL, 0, 0 }, { "badcount2", TEST_ARRAY_AND_SIZE(test_hidl_vec_badcount2_buf), TEST_ARRAY_AND_SIZE(test_hidl_vec_2offsets), NULL, 0, 0 } }; static void test_hidl_vec( gconstpointer test_data) { const TestHidlVec* test = test_data; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(test->in, test->in_size), test->in_size, NULL); GBinderReaderData data; GBinderReader reader; gsize n = 0, elem = 0; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); if (test->offset) { guint i; data.objects = g_new(void*, test->offset_count + 1); for (i = 0; i < test->offset_count; i++) { data.objects[i] = (guint8*)buf->data + test->offset[i]; } data.objects[i] = NULL; } gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_hidl_vec(&reader, &n, &elem) == test->data); g_assert(n == test->count); g_assert(elem == test->elemsize); if (test->data) { n = 42; gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(gbinder_reader_read_hidl_vec1(&reader, &n, test->elemsize) == test->data); g_assert(n == test->count); /* Test invalid expected size */ gbinder_reader_init(&reader, &data, 0, test->in_size); if (test->count) { g_assert(!gbinder_reader_read_hidl_vec1(&reader, NULL, test->elemsize + 1)); } else { /* If total size is zero, we can't really check the element size */ g_assert(gbinder_reader_read_hidl_vec1(&reader, NULL, test->elemsize + 1) == test->data); } } else { gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(!gbinder_reader_read_hidl_vec1(&reader, &n, test->elemsize)); } g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_string_err *==========================================================================*/ typedef struct test_hidl_string_err { const char* name; const guint8* in; guint in_size; const guint* offset; guint offset_count; } TestHidlStringErr; static const guint8 test_hidl_string_err_short [] = { 0x00 }; static const guint8 test_hidl_string_err_bad_obj [] = { TEST_INT32_BYTES(BINDER_TYPE_HANDLE), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const guint8 test_hidl_string_err_empty [] = { TEST_INT32_BYTES(BINDER_TYPE_PTR), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const guint test_hidl_string_err_bad_offset [] = { 100 }; static const guint test_hidl_string_err_one_offset [] = { 0 }; static const TestHidlStringErr test_hidl_string_err_tests [] = { { "no-data", TEST_ARRAY_AND_SIZE(test_hidl_string_err_short), NULL }, { "no-object", TEST_ARRAY_AND_SIZE(test_hidl_string_err_bad_obj), NULL }, { "no-offset", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty), NULL }, { "empty-offset", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty), test_hidl_string_err_one_offset, 0 }, { "bad-offset", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty), TEST_ARRAY_AND_COUNT(test_hidl_string_err_bad_offset) }, { "short-buffer", TEST_ARRAY_AND_SIZE(test_hidl_string_err_short), TEST_ARRAY_AND_COUNT(test_hidl_string_err_one_offset) }, { "empty-buffer", TEST_ARRAY_AND_SIZE(test_hidl_string_err_empty), TEST_ARRAY_AND_COUNT(test_hidl_string_err_one_offset) } }; static void test_hidl_string_err( gconstpointer test_data) { const TestHidlStringErr* test = test_data; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(test->in, test->in_size), test->in_size, NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); if (test->offset) { guint i; data.objects = g_new(void*, test->offset_count + 1); for (i = 0; i < test->offset_count; i++) { data.objects[i] = (guint8*)buf->data + test->offset[i]; } data.objects[i] = NULL; } gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(!gbinder_reader_read_hidl_string(&reader)); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } static void test_hidl_string_err_skip( gconstpointer test_data) { const TestHidlStringErr* test = test_data; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(test->in, test->in_size), test->in_size, NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); if (test->offset) { guint i; data.objects = g_new(void*, test->offset_count + 1); for (i = 0; i < test->offset_count; i++) { data.objects[i] = (guint8*)buf->data + test->offset[i]; } data.objects[i] = NULL; } gbinder_reader_init(&reader, &data, 0, test->in_size); g_assert(!gbinder_reader_skip_hidl_string(&reader)); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * fd_ok *==========================================================================*/ static void test_fd_ok( void) { /* Using 64-bit I/O */ const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_FD), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_fd(&reader) == fd); gbinder_driver_close_fds(ipc->driver, data.objects, (guint8*)buf->data + buf->size); /* The above call must have closed the descriptor */ g_assert(close(fd) < 0); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * fd_shortbuf *==========================================================================*/ static void test_fd_shortbuf( void) { /* Using 64-bit I/O */ const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_FD), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_fd(&reader) < 0); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * fd_badtype *==========================================================================*/ static void test_fd_badtype( void) { /* Using 64-bit I/O */ const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_fd(&reader) < 0); gbinder_driver_close_fds(ipc->driver, data.objects, (guint8*)buf->data + buf->size); /* The above call doesn't close the descriptor */ g_assert(close(fd) == 0); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * dupfd_ok *==========================================================================*/ static void test_dupfd_ok( void) { /* Using 64-bit I/O */ const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_FD), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; int fd2; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); fd2 = gbinder_reader_read_dup_fd(&reader); g_assert(fd2 >= 0); g_assert(fd2 != fd); gbinder_driver_close_fds(ipc->driver, data.objects, (guint8*)buf->data + buf->size); /* The above call closes fd*/ g_assert(close(fd) < 0); g_assert(close(fd2) == 0); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * dupfd_badtype *==========================================================================*/ static void test_dupfd_badtype( void) { /* Using 64-bit I/O */ const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_dup_fd(&reader) < 0); gbinder_driver_close_fds(ipc->driver, data.objects, (guint8*)buf->data + buf->size); /* The above call doesn't close fd*/ g_assert(close(fd) == 0); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * dupfd_badfd *==========================================================================*/ static void test_dupfd_badfd( void) { /* Using 64-bit I/O */ const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); const guint8 input[] = { TEST_INT32_BYTES(BINDER_TYPE_FD), TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); /* Invalidate the descriptor by closing it */ g_assert(close(fd) == 0); g_assert(gbinder_reader_read_dup_fd(&reader) < 0); gbinder_driver_close_fds(ipc->driver, data.objects, (guint8*)buf->data + buf->size); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_string *==========================================================================*/ static void test_hidl_string( const guint8* input, gsize size, const guint* offsets, guint bufcount, const char* result) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size), size, NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; guint i; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, bufcount + 1); for (i = 0; i < bufcount; i++) { data.objects[i] = buf->data + offsets[i]; } data.objects[i] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_hidl_string_c(&reader) == result); g_free(data.objects); gbinder_remote_object_unref(obj); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } static void test_hidl_string1( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), contents); } static void test_hidl_string2( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Invalid object type */ TEST_INT32_BYTES(BINDER_TYPE_HANDLE), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } static void test_hidl_string3( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* No parent */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } static void test_hidl_string4( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Invalid length */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(sizeof(contents) - 1), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } static void test_hidl_string5( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Invalid pointer */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents + 1), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } static void test_hidl_string6( void) { /* No NULL-terminated */ const char contents[] = "testx"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = 4, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(5), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } static void test_hidl_string7( void) { const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Invalid parent offset */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)contents), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET + 1) }; static const guint offsets[] = { 0, BUFFER_OBJECT_SIZE_64 }; test_hidl_string(TEST_ARRAY_AND_SIZE(input), TEST_ARRAY_AND_COUNT(offsets), NULL); } /*==========================================================================* * buffer *==========================================================================*/ static void test_buffer( void) { /* Using 64-bit I/O */ const int data1 = 0x1234; const int data2 = 0x5678; const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&data1), TEST_INT64_BYTES(sizeof(data1)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)&data2), TEST_INT64_BYTES(sizeof(data2)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Not a buffer object */ TEST_BINDER_OBJECT_64(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; GBinderBuffer* res; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 4); data.objects[0] = buf->data; data.objects[1] = buf->data + BUFFER_OBJECT_SIZE_64; data.objects[2] = buf->data + 2 * BUFFER_OBJECT_SIZE_64; data.objects[3] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_skip_buffer(&reader)); res = gbinder_reader_read_buffer(&reader); g_assert(res); g_assert(res->data == &data2); /* The next one is not a buffer object */ g_assert(!gbinder_reader_skip_buffer(&reader)); gbinder_buffer_free(res); g_free(data.objects); gbinder_remote_object_unref(obj); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * parcelable *==========================================================================*/ static void test_parcelable1( void) { const guint8 input_null_header[] = { TEST_INT32_BYTES(kNullParcelableFlag) }; const guint8 input_broken_header[] = { TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(1) }; const guint8 input_non_null_header[] = { TEST_INT32_BYTES(kNonNullParcelableFlag), /* Size must be size of itself + payload */ TEST_INT32_BYTES(sizeof(gint32) * 3) }; const guint8 input_non_null_payload[] = { TEST_INT32_BYTES(10), TEST_INT32_BYTES(20) }; gpointer in; gboolean ok, non_null; gsize size, in_size, out_size; const void* out; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderReader reader, p; GBinderReaderData data; gint32 value[2]; void* objects[1]; g_assert(driver); memset(&data, 0, sizeof(data)); /* Missing payload */ test_init_reader(driver, &data, &reader, TEST_ARRAY_AND_SIZE(input_non_null_header)); out_size = 1; ok = TRUE; g_assert_null(gbinder_reader_read_parcelable(&reader, &out_size)); g_assert_null(gbinder_reader_read_parcelable2(&reader, &out_size, &ok)); g_assert_cmpuint(gbinder_reader_bytes_read(&reader), == ,0); g_assert_cmpuint(out_size, == ,0); g_assert_false(ok); gbinder_buffer_free(data.buffer); /* Broken headers */ test_init_reader(driver, &data, &reader, input_broken_header, 4); out_size = 1; ok = TRUE; g_assert_null(gbinder_reader_read_parcelable(&reader, &out_size)); g_assert_null(gbinder_reader_read_parcelable2(&reader, &out_size, &ok)); g_assert_cmpuint(gbinder_reader_bytes_read(&reader), == ,0); g_assert_cmpuint(out_size, == ,0); g_assert_false(ok); gbinder_buffer_free(data.buffer); test_init_reader(driver, &data, &reader, TEST_ARRAY_AND_SIZE(input_broken_header)); out_size = 1; ok = TRUE; g_assert_null(gbinder_reader_read_parcelable(&reader, &out_size)); g_assert_null(gbinder_reader_read_parcelable2(&reader, &out_size, &ok)); g_assert_cmpuint(gbinder_reader_bytes_read(&reader), == ,0); g_assert_cmpuint(out_size, == ,0); g_assert_false(ok); gbinder_buffer_free(data.buffer); /* Null */ test_init_reader(driver, &data, &reader, TEST_ARRAY_AND_SIZE(input_null_header)); gbinder_reader_init(&reader, &data, 0, sizeof(input_null_header)); g_assert_null(gbinder_reader_read_parcelable2(&reader, NULL, NULL)); g_assert_cmpuint(gbinder_reader_bytes_read(&reader), > ,0); g_assert_true(gbinder_reader_at_end(&reader)); gbinder_reader_init(&reader, &data, 0, sizeof(input_null_header)); out_size = 1; ok = FALSE; g_assert_null(gbinder_reader_read_parcelable2(&reader, &out_size, &ok)); g_assert_true(gbinder_reader_at_end(&reader)); g_assert_cmpuint(out_size, == ,0); g_assert_true(ok); gbinder_reader_init(&reader, &data, 0, sizeof(input_null_header)); non_null = TRUE; g_assert_true(gbinder_reader_start_parcelable(&reader, &p, &non_null)); g_assert_false(non_null); g_assert(gbinder_reader_at_end(&reader)); g_assert(gbinder_reader_at_end(&p)); gbinder_reader_finish_parcelable(&p); gbinder_buffer_free(data.buffer); /* Non-null */ in_size = sizeof(input_non_null_header) + sizeof(input_non_null_payload); in = g_malloc(in_size); memcpy(in, &input_non_null_header, sizeof(input_non_null_header)); memcpy(in + sizeof(input_non_null_header), &input_non_null_payload, sizeof(input_non_null_payload)); data.buffer = gbinder_buffer_new(driver, in, in_size, NULL); gbinder_reader_init(&reader, &data, 0, in_size); out_size = 0; ok = FALSE; out = gbinder_reader_read_parcelable2(&reader, &out_size, &ok); g_assert_true(ok); g_assert_cmpmem(&input_non_null_payload, sizeof(input_non_null_payload), out, out_size); g_assert_true(gbinder_reader_at_end(&reader)); /* And verify NULL tolerance for the size parameter */ gbinder_reader_init(&reader, &data, 0, in_size); out = gbinder_reader_read_parcelable(&reader, NULL); g_assert(memcmp(&input_non_null_payload, out, sizeof(input_non_null_payload)) == 0); /* Test the nested reader */ objects[0] = NULL; /* empty but non-null object list */ data.objects = objects; gbinder_reader_init(&reader, &data, 0, in_size); non_null = FALSE; g_assert_true(gbinder_reader_start_parcelable(&reader, &p, &non_null)); g_assert_true(non_null); g_assert_true(gbinder_reader_at_end(&reader)); memset(value, 0, sizeof(value)); g_assert(gbinder_reader_read_int32(&p, value)); g_assert(gbinder_reader_read_int32(&p, value + 1)); g_assert(gbinder_reader_at_end(&p)); g_assert_cmpuint(value[0], == ,10); g_assert_cmpuint(value[1], == ,20); gbinder_reader_finish_parcelable(&p); /* Poke the same thing with gbinder_reader_read_parcelable */ gbinder_reader_init(&reader, &data, 0, in_size); g_assert_nonnull(gbinder_reader_read_parcelable(&reader, &size)); g_assert_cmpuint(size, == ,sizeof(input_non_null_payload)); g_assert_true(gbinder_reader_at_end(&reader)); /* And gbinder_reader_skip_parcelable */ gbinder_reader_init(&reader, &data, 0, in_size); g_assert_true(gbinder_reader_skip_parcelable(&reader)); g_assert_true(gbinder_reader_at_end(&reader)); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } static void test_parcelable2( void) { /* Using 64-bit I/O */ const guint8 input[] = { /* * parcelable p1 { * parcelable p2 { * object obj1; * parcelable p3 { * object obj2; * }; * }; * object obj3; * }; * * parcelable p4 { * int i1 = 40; * parcelable p5 { * int i2 = 41; * object obj4; * }; * }; * * int i3 = 42; * object obj5; * * parcelable p6 { * int i4 = 43; * object obj6; * }; * * object obj7; * parcelable p7 = null; * parcelable p8 {}; */ /* Parcelable #1 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + 8 /* parcelable #2 header */ + BINDER_OBJECT_SIZE_64 /* obj1 */ + 8 /* parcelable #3 header */ + BINDER_OBJECT_SIZE_64 /* obj2 */ + BINDER_OBJECT_SIZE_64 /* obj3 */), /* Parcelable #2 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + BINDER_OBJECT_SIZE_64 /* obj1 */ + 8 /* parcelable #3 header */ + BINDER_OBJECT_SIZE_64 /* obj2 */), TEST_BINDER_OBJECT_64(1), /* obj1 */ /* Parcelable #3 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + BINDER_OBJECT_SIZE_64 /* obj2 */), TEST_BINDER_OBJECT_64(2), /* obj2 */ /* End of parcelable #3 */ /* End of parcelable #2 */ TEST_BINDER_OBJECT_64(3), /* obj3 */ /* End of parcelable #1 */ /* Parcelable #4 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + 4 /* i1 */ + 8 /* parcelable #5 header */ + 4 /* i2 */ + BINDER_OBJECT_SIZE_64 /* obj4 */), TEST_INT32_BYTES(40), /* i1 */ /* Parcelable #5 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + 4 /* i2 */ + BINDER_OBJECT_SIZE_64 /* obj4 */), TEST_INT32_BYTES(41), /* i2 */ TEST_BINDER_OBJECT_64(4), /* obj4 */ /* End of parcelable #5 */ /* End of parcelable #4 */ TEST_INT32_BYTES(42), /* i3 */ TEST_BINDER_OBJECT_64(5), /* obj5 */ /* Parcelable #6 */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */ + 4 /* i4 */ + BINDER_OBJECT_SIZE_64 /* obj6 */), TEST_INT32_BYTES(43), TEST_BINDER_OBJECT_64(6), /* obj6 */ /* End of parcelable #6 */ TEST_BINDER_OBJECT_64(7), /* obj7 */ /* Parcelable #7 (null) */ TEST_INT32_BYTES(kNullParcelableFlag), /* Parcelable #8 (empty) */ TEST_INT32_BYTES(kNonNullParcelableFlag), TEST_INT32_BYTES(4 /* this field */) }; const gsize object1_offset = 8 /* parcelable #1 header */ + 8 /* parcelable #2 header */; const gsize object2_offset = object1_offset + BINDER_OBJECT_SIZE_64 /* binder object #1 */ + 8 /* parcelable #3 header */; const gsize object3_offset = object2_offset + BINDER_OBJECT_SIZE_64 /* binder object #2 */; const gsize object4_offset = object3_offset + BINDER_OBJECT_SIZE_64 /* binder object #3 */ + 8 /* parcelable #4 header */ + 4 /* i1 */ + 8 /* parcelable #5 header */ + 4 /* i2 */; const gsize object5_offset = object4_offset + BINDER_OBJECT_SIZE_64 /* binder object #4 */ + 4 /* i3 */; const gsize object6_offset = object5_offset + BINDER_OBJECT_SIZE_64 /* binder object #5 */ + 8 /* parcelable #6 header */ + 4 /* i4 */; const gsize object7_offset = object6_offset + BINDER_OBJECT_SIZE_64 /* binder object #6 */; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader, p1, p2, p3, p4; gboolean ok, non_null; guint32 val; gsize size; void* objects[8]; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = objects; objects[0] = buf->data + object1_offset; objects[1] = buf->data + object2_offset; objects[2] = buf->data + object3_offset; objects[3] = buf->data + object4_offset; objects[4] = buf->data + object5_offset; objects[5] = buf->data + object6_offset; objects[6] = buf->data + object7_offset; objects[7] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); /* Nested reader for parcelable #1 */ non_null = FALSE; g_assert_true(gbinder_reader_start_parcelable(&reader, &p1, &non_null)); g_assert_true(non_null); /* Nested reader for parcelable #2 */ non_null = FALSE; g_assert_true(gbinder_reader_start_parcelable(&p1, &p2, &non_null)); g_assert_true(non_null); /* Read binder object #1 from parcelable #2 */ g_assert_true(gbinder_reader_read_nullable_object(&p2, &obj)); g_assert_nonnull(obj); g_assert_cmpuint(obj->handle, == ,1); gbinder_remote_object_unref(obj); /* Nested reader for parcelable #3 */ non_null = FALSE; g_assert_true(gbinder_reader_start_parcelable(&p2, &p3, &non_null)); g_assert_true(non_null); g_assert_false(gbinder_reader_at_end(&p3)); /* Skip binder object #2 */ gbinder_reader_finish_parcelable(&p3); /* done with parcelable #3 */ gbinder_reader_finish_parcelable(&p2); /* done with parcelable #2 */ g_assert_true(gbinder_reader_at_end(&p3)); g_assert_true(gbinder_reader_at_end(&p2)); /* Read binder object #3 from parcelable #1 */ g_assert_true(gbinder_reader_read_nullable_object(&p1, &obj)); g_assert_nonnull(obj); g_assert_cmpuint(obj->handle, == ,3); gbinder_remote_object_unref(obj); gbinder_reader_finish_parcelable(&p1); /* done with parcelable #1 */ /* Parcelable #4 */ g_assert_true(gbinder_reader_start_parcelable(&reader, &p4, NULL)); g_assert_false(gbinder_reader_at_end(&p4)); /* i1 */ val = 0; g_assert_true(gbinder_reader_read_uint32(&p4, &val)); g_assert_cmpuint(val, == ,40); /* Parcelable #5 */ ok = FALSE; size = 0; g_assert_nonnull(gbinder_reader_read_parcelable2(&p4, &size, &ok)); g_assert_cmpuint(size, == ,4 /* i2 */ + BINDER_OBJECT_SIZE_64 /* obj4 */); g_assert_true(ok); /* Object #4 gets skipped */ /* i3 */ val = 0; g_assert_true(gbinder_reader_read_uint32(&reader, &val)); g_assert_cmpuint(val, == ,42); /* obj5 */ g_assert_true(gbinder_reader_read_nullable_object(&reader, &obj)); g_assert_nonnull(obj); g_assert_cmpuint(obj->handle, == ,5); gbinder_remote_object_unref(obj); /* Parcelable #6 */ g_assert_true(gbinder_reader_skip_parcelable(&reader)); /* Object #6 gets skipped */ /* obj7 */ g_assert_true(gbinder_reader_read_nullable_object(&reader, &obj)); g_assert_nonnull(obj); g_assert_cmpuint(obj->handle, == ,7); gbinder_remote_object_unref(obj); /* Parcelable #7 (null) */ ok = FALSE; g_assert_null(gbinder_reader_read_parcelable2(&reader, NULL, &ok)); g_assert_true(ok); /* Parcelable #8 (empty) */ ok = FALSE; g_assert_nonnull(gbinder_reader_read_parcelable2(&reader, &size, &ok)); g_assert_cmpuint(size, == ,0); g_assert_true(ok); /* Now we must have reached the end */ g_assert_true(gbinder_reader_at_end(&reader)); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * object *==========================================================================*/ static void test_object( void) { /* Using 64-bit I/O */ static const guint8 input[] = { TEST_BINDER_OBJECT_64(1) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(gbinder_reader_read_nullable_object(&reader, &obj)); g_assert(obj); g_assert_cmpuint(obj->handle, == ,1); g_free(data.objects); gbinder_remote_object_unref(obj); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_object_no_reg *==========================================================================*/ static void test_object_no_reg( void) { GBinderReaderData data; GBinderReader reader; memset(&data, 0, sizeof(data)); gbinder_reader_init(&reader, &data, 0, 0); g_assert(!gbinder_reader_read_hidl_string(&reader)); g_assert(!gbinder_reader_read_object(&reader)); } /*==========================================================================* * object_invalid *==========================================================================*/ static void test_object_invalid( void) { /* Using 64-bit I/O */ static const guint8 input[] = { TEST_INT32_BYTES(42 /* invalid type */), TEST_INT32_BYTES(0), TEST_INT64_BYTES(1 /* handle*/), TEST_INT64_BYTES(0) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(!gbinder_reader_read_nullable_object(&reader, &obj)); g_assert(!obj); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } static void test_object_truncated_aidl( void) { /* AIDL3 requires a 4-byte stability trailer after flat_binder_object. */ static const guint8 input[] = { TEST_BINDER_OBJECT_64(1) }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, "aidl3"); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, sizeof(input)), sizeof(input), NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = buf->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); g_assert(!gbinder_reader_read_nullable_object(&reader, &obj)); g_assert(!obj); g_free(data.objects); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * vec *==========================================================================*/ static void test_vec( void) { /* Using 64-bit I/O */ GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderReaderData data; GBinderReader reader; BinderObject64 obj; GBinderHidlVec vec; char** out; g_assert(ipc); memset(&data, 0, sizeof(data)); memset(&vec, 0, sizeof(vec)); memset(&obj, 0, sizeof(obj)); obj.type = BINDER_TYPE_PTR; obj.buffer.ptr = &vec; /* This one will fail because the buffer is one byte short */ obj.length = sizeof(vec) - 1; data.buffer = gbinder_buffer_new(ipc->driver, g_memdup(&obj, sizeof(obj)), sizeof(obj), NULL); data.reg = gbinder_ipc_object_registry(ipc); data.objects = g_new(void*, 2); data.objects[0] = data.buffer->data; data.objects[1] = NULL; gbinder_reader_init(&reader, &data, 0, data.buffer->size); g_assert(!gbinder_reader_read_hidl_string_vec(&reader)); /* This one will read empty array */ obj.length = sizeof(vec); gbinder_buffer_free(data.buffer); data.buffer = gbinder_buffer_new(ipc->driver, g_memdup(&obj, sizeof(obj)), sizeof(obj), NULL); data.objects[0] = data.buffer->data; gbinder_reader_init(&reader, &data, 0, data.buffer->size); out = gbinder_reader_read_hidl_string_vec(&reader); g_assert(out); g_assert(!out[0]); g_strfreev(out); g_free(data.objects); gbinder_buffer_free(data.buffer); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * hidl_string_vec *==========================================================================*/ static void test_hidl_string_vec( const guint8* input, gsize size, const char* const* result) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size), size, NULL); GBinderRemoteObject* obj = NULL; GBinderReaderData data; GBinderReader reader; char** out; guint i; g_assert(ipc); memset(&data, 0, sizeof(data)); data.buffer = buf; data.reg = gbinder_ipc_object_registry(ipc); /* We assume that input consists only from buffer objects */ g_assert(!(size % BUFFER_OBJECT_SIZE_64)); data.objects = g_new(void*, size/BUFFER_OBJECT_SIZE_64 + 1); for (i = 0; i < size/BUFFER_OBJECT_SIZE_64; i++) { data.objects[i] = buf->data + i * BUFFER_OBJECT_SIZE_64; } data.objects[i] = NULL; gbinder_reader_init(&reader, &data, 0, buf->size); out = gbinder_reader_read_hidl_string_vec(&reader); if (out) { const guint n = g_strv_length(out); g_assert(result); g_assert(n == g_strv_length((char**)result)); for (i = 0; i < n; i++) { g_assert(!g_strcmp0(out[i], result[i])); } } else { g_assert(!result); } g_strfreev(out); g_free(data.objects); gbinder_remote_object_unref(obj); gbinder_buffer_free(buf); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } static void test_hidl_string_vec1( void) { static const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; const GBinderHidlVec vec = { .data = { .ptr = &str }, .count = 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&vec), TEST_INT64_BYTES(sizeof(vec)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(1), TEST_INT64_BYTES(GBINDER_HIDL_VEC_BUFFER_OFFSET), /* Buffer object #3 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)&contents), TEST_INT64_BYTES(sizeof(contents)), TEST_INT64_BYTES(2), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const char* const result[] = { contents, NULL }; test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), result); } static void test_hidl_string_vec2( void) { static const char str1[] = "meh"; static const char str2[] = "foobar"; const GBinderHidlString str[] = { { .data = { (uintptr_t)str1 }, .len = sizeof(str1) - 1, .owns_buffer = TRUE },{ .data = { (uintptr_t)str2 }, .len = sizeof(str2) - 1, .owns_buffer = TRUE } }; const GBinderHidlVec vec = { .data = { .ptr = &str }, .count = 2, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&vec), TEST_INT64_BYTES(sizeof(vec)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(1), TEST_INT64_BYTES(GBINDER_HIDL_VEC_BUFFER_OFFSET), /* Buffer object #3 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str1), TEST_INT64_BYTES(sizeof(str1)), TEST_INT64_BYTES(2), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET), /* Buffer object #4 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str2), TEST_INT64_BYTES(sizeof(str2)), TEST_INT64_BYTES(2), TEST_INT64_BYTES(sizeof(GBinderHidlString) + GBINDER_HIDL_STRING_BUFFER_OFFSET) }; static const char* const result[] = { str1, str2, NULL }; test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), result); } static void test_hidl_string_vec3( void) { static const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; const GBinderHidlVec vec = { .data = { .ptr = &str }, .count = 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&vec), TEST_INT64_BYTES(sizeof(vec)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)&str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(1), TEST_INT64_BYTES(GBINDER_HIDL_VEC_BUFFER_OFFSET) /* The next buffer is missing */ }; test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), NULL); } static void test_hidl_string_vec4( void) { static const char contents[] = "test"; const GBinderHidlString str = { .data = { (uintptr_t)contents }, .len = sizeof(contents) - 1, .owns_buffer = TRUE }; const GBinderHidlVec vec = { .data = { .ptr = &str }, .count = 1, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&vec), TEST_INT64_BYTES(sizeof(vec)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* The next buffer is missing */ }; test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), NULL); } static void test_hidl_string_vec5( void) { static const char str1[] = "meh"; static const char str2[] = "foobar"; const GBinderHidlString str[] = { { .data = { (uintptr_t)str1 }, .len = sizeof(str1) - 1, .owns_buffer = TRUE },{ .data = { (uintptr_t)str2 }, .len = sizeof(str2) - 1, .owns_buffer = TRUE } }; const GBinderHidlVec vec = { .data = { .ptr = &str }, .count = 2, .owns_buffer = TRUE }; /* Using 64-bit I/O */ const guint8 input[] = { /* Buffer object #1 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(0), TEST_INT64_BYTES((uintptr_t)&vec), TEST_INT64_BYTES(sizeof(vec)), TEST_INT64_BYTES(0), TEST_INT64_BYTES(0), /* Buffer object #2 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str), TEST_INT64_BYTES(sizeof(str)), TEST_INT64_BYTES(1), TEST_INT64_BYTES(GBINDER_HIDL_VEC_BUFFER_OFFSET), /* Buffer object #3 */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str1), TEST_INT64_BYTES(sizeof(str1)), TEST_INT64_BYTES(2), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET), /* Buffer object #4 (with invalid offset) */ TEST_INT32_BYTES(BINDER_TYPE_PTR), TEST_INT32_BYTES(BINDER_BUFFER_FLAG_HAS_PARENT), TEST_INT64_BYTES((uintptr_t)str2), TEST_INT64_BYTES(sizeof(str2)), TEST_INT64_BYTES(2), TEST_INT64_BYTES(GBINDER_HIDL_STRING_BUFFER_OFFSET) }; test_hidl_string_vec(TEST_ARRAY_AND_SIZE(input), NULL); } /*==========================================================================* * byte_array *==========================================================================*/ static void test_byte_array( void) { const char in_data[] = "1234abcd"; gint32 in_len = sizeof(in_data) - 1; const void* out_data = NULL; gsize out_len = 0; void* tmp; gsize tmp_len = sizeof(in_len) + in_len; gint32 null_len = -1; GBinderDriver* driver; GBinderReader reader; GBinderReaderData data; /* test for failed read (wrong len part of byte array) */ g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL))); tmp = g_malloc0(1); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, tmp, 1, NULL); gbinder_reader_init(&reader, &data, 0, 1); g_assert(!gbinder_reader_read_byte_array(&reader, &out_len)); g_assert(!gbinder_reader_at_end(&reader)); g_assert(out_len == 0); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); /* test for failed read (wrong data part of byte array) */ g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL))); tmp = g_malloc0(in_len - 1); memcpy(tmp, &in_len, sizeof(in_len)); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, tmp, in_len - 1, NULL); gbinder_reader_init(&reader, &data, 0, in_len - 1); g_assert(!gbinder_reader_read_byte_array(&reader, &out_len)); g_assert(!gbinder_reader_at_end(&reader)); g_assert(out_len == 0); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); /* test for empty (len 0) byte array */ g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL))); tmp = g_malloc0(sizeof(null_len)); memcpy(tmp, &null_len, sizeof(null_len)); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, tmp, sizeof(null_len), NULL); gbinder_reader_init(&reader, &data, 0, sizeof(null_len)); g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len))); g_assert(gbinder_reader_at_end(&reader)); g_assert(out_len == 0); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); /* test for data */ g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL))); tmp = g_malloc0(tmp_len); memcpy(tmp, &in_len, sizeof(in_len)); memcpy(tmp + sizeof(in_len), in_data, in_len); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, tmp, tmp_len, NULL); gbinder_reader_init(&reader, &data, 0, tmp_len); g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len))); g_assert(gbinder_reader_at_end(&reader)); g_assert((gsize)in_len == out_len); g_assert(memcmp(in_data, out_data, in_len) == 0); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * copy *==========================================================================*/ static void test_copy( void) { const char in_data1[] = "12345678"; const char in_data2[] = "abcdefgh"; gint32 in_len1 = sizeof(in_data1) - 1; gint32 in_len2 = sizeof(in_data2) - 1; const void* out_data = NULL; gsize out_len = 0; guint8* ptr; void* buf; gsize buf_len = 2 * sizeof(guint32) + in_len1 + in_len2; GBinderDriver* driver; GBinderReader reader; GBinderReader reader2; GBinderReaderData data; /* test for data */ g_assert((driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL))); ptr = buf = g_malloc0(buf_len); memcpy(ptr, &in_len1, sizeof(in_len1)); ptr += sizeof(in_len1); memcpy(ptr, in_data1, in_len1); ptr += in_len1; memcpy(ptr, &in_len2, sizeof(in_len2)); ptr += sizeof(in_len2); memcpy(ptr, in_data2, in_len2); memset(&data, 0, sizeof(data)); data.buffer = gbinder_buffer_new(driver, buf, buf_len, NULL); gbinder_reader_init(&reader, &data, 0, buf_len); /* Read the first array */ g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len))); g_assert_cmpuint(in_len1, == ,out_len); g_assert(memcmp(in_data1, out_data, in_len1) == 0); /* Fetch raw data */ g_assert((out_data = gbinder_reader_get_data(&reader, &out_len))); g_assert(out_data == gbinder_reader_get_data(&reader, NULL)); g_assert_cmpuint(out_len, == ,buf_len); g_assert(memcmp(out_data, buf, buf_len) == 0); /* Copy the reader */ gbinder_reader_copy(&reader2, &reader); /* Read both and compare the output */ g_assert((out_data = gbinder_reader_read_byte_array(&reader, &out_len))); g_assert(gbinder_reader_at_end(&reader)); g_assert_cmpuint(in_len1, == ,out_len); g_assert(memcmp(in_data2, out_data, in_len2) == 0); g_assert((out_data = gbinder_reader_read_byte_array(&reader2, &out_len))); g_assert(gbinder_reader_at_end(&reader2)); g_assert_cmpuint(in_len2, == ,out_len); g_assert(memcmp(in_data2, out_data, in_len2) == 0); /* Fetch raw data from the copied reader */ g_assert((out_data = gbinder_reader_get_data(&reader2, &out_len))); g_assert(out_data == gbinder_reader_get_data(&reader2, NULL)); g_assert_cmpuint(out_len, == ,buf_len); g_assert(memcmp(out_data, buf, buf_len) == 0); gbinder_buffer_free(data.buffer); gbinder_driver_unref(driver); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/reader/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { guint i; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("empty"), test_empty); g_test_add_func(TEST_("byte"), test_byte); g_test_add_func(TEST_("bool"), test_bool); g_test_add_func(TEST_("int8"), test_int8); g_test_add_func(TEST_("int16"), test_int16); g_test_add_func(TEST_("int32"), test_int32); g_test_add_func(TEST_("int64"), test_int64); g_test_add_func(TEST_("float"), test_float); g_test_add_func(TEST_("double"), test_double); for (i = 0; i < G_N_ELEMENTS(test_cstring_tests); i++) { const TestStringData* test = test_cstring_tests + i; char* path = g_strconcat(TEST_("cstring/"), test->name, NULL); g_test_add_data_func(path, test, test_cstring); g_free(path); } g_test_add_func(TEST_("string8/null"), test_string8_null); for (i = 0; i < G_N_ELEMENTS(test_string8_tests); i++) { const TestStringData* test = test_string8_tests + i; char* path = g_strconcat(TEST_("string8/"), test->name, NULL); g_test_add_data_func(path, test, test_string8); g_free(path); } g_test_add_func(TEST_("string16/null"), test_string16_null); for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) { const TestStringData* test = test_string16_tests + i; char* path = g_strconcat(TEST_("string16/"), test->name, NULL); g_test_add_data_func(path, test, test_string16); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_hidl_struct_tests); i++) { const TestHidlStruct* test = test_hidl_struct_tests + i; char* path = g_strconcat(TEST_("hidl_struct/"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_struct); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) { const TestHidlVec* test = test_hidl_vec_tests + i; char* path = g_strconcat(TEST_("hidl_vec/"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_vec); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_hidl_string_err_tests); i++) { const TestHidlStringErr* test = test_hidl_string_err_tests + i; char* path = g_strconcat(TEST_("hidl_string/err-"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_string_err); g_free(path); path = g_strconcat(TEST_("hidl_string/err-skip-"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_string_err_skip); g_free(path); } g_test_add_func(TEST_("fd/ok"), test_fd_ok); g_test_add_func(TEST_("fd/shortbuf"), test_fd_shortbuf); g_test_add_func(TEST_("fd/badtype"), test_fd_badtype); g_test_add_func(TEST_("dupfd/ok"), test_dupfd_ok); g_test_add_func(TEST_("dupfd/badtype"), test_dupfd_badtype); g_test_add_func(TEST_("dupfd/badfd"), test_dupfd_badfd); g_test_add_func(TEST_("hidl_string/1"), test_hidl_string1); g_test_add_func(TEST_("hidl_string/2"), test_hidl_string2); g_test_add_func(TEST_("hidl_string/3"), test_hidl_string3); g_test_add_func(TEST_("hidl_string/4"), test_hidl_string4); g_test_add_func(TEST_("hidl_string/5"), test_hidl_string5); g_test_add_func(TEST_("hidl_string/6"), test_hidl_string6); g_test_add_func(TEST_("hidl_string/7"), test_hidl_string7); g_test_add_func(TEST_("buffer"), test_buffer); g_test_add_func(TEST_("parcelable/1"), test_parcelable1); g_test_add_func(TEST_("parcelable/2"), test_parcelable2); g_test_add_func(TEST_("object/valid"), test_object); g_test_add_func(TEST_("object/invalid"), test_object_invalid); g_test_add_func(TEST_("object/truncated_aidl"), test_object_truncated_aidl); g_test_add_func(TEST_("object/no_reg"), test_object_no_reg); g_test_add_func(TEST_("vec"), test_vec); g_test_add_func(TEST_("hidl_string_vec/1"), test_hidl_string_vec1); g_test_add_func(TEST_("hidl_string_vec/2"), test_hidl_string_vec2); g_test_add_func(TEST_("hidl_string_vec/3"), test_hidl_string_vec3); g_test_add_func(TEST_("hidl_string_vec/4"), test_hidl_string_vec4); g_test_add_func(TEST_("hidl_string_vec/5"), test_hidl_string_vec5); g_test_add_func(TEST_("byte_array"), test_byte_array); g_test_add_func(TEST_("copy"), test_copy); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_remote_object/000077500000000000000000000000001526127616700205575ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_remote_object/Makefile000066400000000000000000000001251526127616700222150ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_remote_object include ../common/Makefile libgbinder-1.1.54/unit/unit_remote_object/unit_remote_object.c000066400000000000000000000132261526127616700246070ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_object_registry.h" #include "gbinder_remote_object_p.h" #include static TestOpt test_opt; /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { g_assert(!gbinder_remote_object_new(NULL, 0, FALSE)); g_assert(!gbinder_remote_object_ref(NULL)); g_assert(!gbinder_remote_object_ipc(NULL)); gbinder_remote_object_unref(NULL); g_assert(gbinder_remote_object_is_dead(NULL)); g_assert(!gbinder_remote_object_add_death_handler(NULL, NULL, NULL)); gbinder_remote_object_remove_handler(NULL, 0); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg,1,TRUE); GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg,2,TRUE); g_assert(obj1); g_assert(obj2); g_assert(obj1->handle == 1u); g_assert(obj2->handle == 2u); g_assert(gbinder_remote_object_ipc(obj1) == ipc); g_assert(gbinder_remote_object_ipc(obj2) == ipc); g_assert(!gbinder_remote_object_is_dead(obj1)); g_assert(gbinder_remote_object_reanimate(obj1)); g_assert(gbinder_remote_object_ref(obj1) == obj1); gbinder_remote_object_unref(obj1); /* Compensate the above reference */ g_assert(!gbinder_remote_object_add_death_handler(obj1, NULL, NULL)); g_assert(gbinder_object_registry_get_remote(reg, 1, FALSE) == obj1); gbinder_remote_object_unref(obj1); /* Compensate the above reference */ gbinder_remote_object_unref(obj1); gbinder_remote_object_unref(obj2); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * dead *==========================================================================*/ static void test_dead_done( GBinderRemoteObject* obj, void* user_data) { GVERBOSE_(""); test_quit_later((GMainLoop*)user_data); } static void test_dead_run( void) { const guint h = 1; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc); const int fd = gbinder_driver_fd(ipc->driver); GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, h, TRUE); gulong id = gbinder_remote_object_add_death_handler (obj, test_dead_done, loop); test_binder_br_dead_binder(fd, ANY_THREAD, h); test_run(&test_opt, loop); g_assert(gbinder_remote_object_is_dead(obj)); gbinder_remote_object_remove_handler(obj, id); gbinder_remote_object_remove_handler(obj, 0); /* has no effect */ gbinder_remote_object_unref(obj); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_dead( void) { test_run_in_context(&test_opt, test_dead_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/remote_object/" int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "basic", test_basic); g_test_add_func(TEST_PREFIX "dead", test_dead); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_remote_reply/000077500000000000000000000000001526127616700204445ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_remote_reply/Makefile000066400000000000000000000001241526127616700221010ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_remote_reply include ../common/Makefile libgbinder-1.1.54/unit/unit_remote_reply/unit_remote_reply.c000066400000000000000000000267001526127616700243620ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2024 Slava Monich * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_buffer_p.h" #include "gbinder_driver.h" #include "gbinder_reader.h" #include "gbinder_local_reply_p.h" #include "gbinder_remote_reply_p.h" #include "gbinder_object_registry.h" #include "gbinder_output_data.h" #include static TestOpt test_opt; /*==========================================================================* * Dummy GBinderObjectRegistry functions *==========================================================================*/ static void reg_dummy_ref_unref( GBinderObjectRegistry* reg) { } static GBinderLocalObject* reg_dummy_get_local( GBinderObjectRegistry* reg, void* pointer) { return NULL; } static GBinderRemoteObject* reg_dummy_get_remote( GBinderObjectRegistry* reg, guint32 handle, REMOTE_REGISTRY_CREATE create) { return NULL; } static GBinderObjectRegistryFunctions reg_dummy_fn = { .ref = reg_dummy_ref_unref, .unref = reg_dummy_ref_unref, .get_local = reg_dummy_get_local, .get_remote = reg_dummy_get_remote }; /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderReader reader; g_assert(!gbinder_remote_reply_ref(NULL)); gbinder_remote_reply_unref(NULL); gbinder_remote_reply_init_reader(NULL, &reader); g_assert(gbinder_reader_at_end(&reader)); g_assert(gbinder_remote_reply_is_empty(NULL)); g_assert(!gbinder_remote_reply_copy_to_local(NULL)); g_assert(!gbinder_remote_reply_read_int32(NULL, NULL)); g_assert(!gbinder_remote_reply_read_uint32(NULL, NULL)); g_assert(!gbinder_remote_reply_read_int64(NULL, NULL)); g_assert(!gbinder_remote_reply_read_uint64(NULL, NULL)); g_assert(!gbinder_remote_reply_read_string8(NULL)); g_assert(!gbinder_remote_reply_read_string16(NULL)); g_assert(!gbinder_remote_reply_read_object(NULL)); } /*==========================================================================* * empty *==========================================================================*/ static void test_empty( void) { GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver, NULL, 0, NULL)); g_assert(gbinder_remote_reply_is_empty(reply)); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { GBinderReader reader; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); gbinder_remote_reply_init_reader(reply, &reader); g_assert(gbinder_reader_at_end(&reader)); g_assert(gbinder_remote_reply_is_empty(reply)); g_assert(gbinder_remote_reply_ref(reply) == reply); g_assert(!gbinder_remote_reply_read_object(reply)); gbinder_remote_reply_unref(reply); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { static const guint8 reply_data [] = { TEST_INT32_BYTES(42) }; guint32 out1 = 0; gint32 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver, g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL)); g_assert(!gbinder_remote_reply_is_empty(reply)); g_assert(gbinder_remote_reply_read_uint32(reply, &out1)); g_assert(gbinder_remote_reply_read_int32(reply, &out2)); g_assert(out1 == 42); g_assert(out2 == 42u); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { static const guint8 reply_data [] = { TEST_INT64_BYTES(42) }; guint64 out1 = 0; gint64 out2 = 0; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver, g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL)); g_assert(!gbinder_remote_reply_is_empty(reply)); g_assert(gbinder_remote_reply_read_uint64(reply, &out1)); g_assert(gbinder_remote_reply_read_int64(reply, &out2)); g_assert(out1 == 42); g_assert(out2 == 42u); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * string8 *==========================================================================*/ static void test_string8( void) { static const guint8 reply_data [] = { 'b', 'a', 'r', 0x00 }; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver, g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL)); g_assert(!gbinder_remote_reply_is_empty(reply)); g_assert(!g_strcmp0(gbinder_remote_reply_read_string8(reply), "bar")); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * string16 *==========================================================================*/ static void test_string16( void) { static const guint8 reply_data [] = { TEST_INT32_BYTES(3), TEST_INT16_BYTES('b'), TEST_INT16_BYTES('a'), TEST_INT16_BYTES('r'), 0x00, 0x00 }; GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* reply = gbinder_remote_reply_new(®); char* str; gbinder_remote_reply_set_data(reply, gbinder_buffer_new(driver, g_memdup(reply_data, sizeof(reply_data)), sizeof(reply_data), NULL)); g_assert(!gbinder_remote_reply_is_empty(reply)); str = gbinder_remote_reply_read_string16(reply); g_assert(!g_strcmp0(str, "bar")); g_free(str); gbinder_remote_reply_unref(reply); gbinder_driver_unref(driver); } /*==========================================================================* * to_local *==========================================================================*/ static void test_to_local( void) { static const guint8 reply_bytes [] = { /* 32-bit integer */ TEST_INT32_BYTES(42), /* 64-bit NULL flat_binder_object */ TEST_INT32_BYTES(BINDER_TYPE_BINDER), /* hdr.type */ TEST_INT32_BYTES(0x17f), /* flags */ TEST_INT64_BYTES(0), /* handle */ TEST_INT64_BYTES(0) /* cookie */ }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderObjectRegistry reg = { ®_dummy_fn, gbinder_driver_io(driver) }; GBinderRemoteReply* rr = gbinder_remote_reply_new(®); GBinderLocalReply* lr; GBinderOutputData* data; const GByteArray* bytes; GUtilIntArray* offsets; guint8* reply_data = g_memdup(reply_bytes, sizeof(reply_bytes)); void** objects = g_new0(void*, 2); /* Skip the 32-bit integer */ objects[0] = reply_data + 4; gbinder_remote_reply_set_data(rr, gbinder_buffer_new(driver, reply_data, sizeof(reply_bytes), objects)); /* Convert to GBinderLocalReply */ lr = gbinder_remote_reply_copy_to_local(rr); data = gbinder_local_reply_data(lr); offsets = gbinder_output_data_offsets(data); bytes = data->bytes; g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 4); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(bytes->len == sizeof(reply_bytes)); gbinder_remote_reply_unref(rr); gbinder_local_reply_unref(lr); gbinder_driver_unref(driver); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/remote_reply/" int main(int argc, char* argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "empty", test_empty); g_test_add_func(TEST_PREFIX "basic", test_basic); g_test_add_func(TEST_PREFIX "int32", test_int32); g_test_add_func(TEST_PREFIX "int64", test_int64); g_test_add_func(TEST_PREFIX "string8", test_string8); g_test_add_func(TEST_PREFIX "string16", test_string16); g_test_add_func(TEST_PREFIX "to_local", test_to_local); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Remote Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_remote_request/000077500000000000000000000000001526127616700210015ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_remote_request/Makefile000066400000000000000000000001261526127616700224400ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_remote_request include ../common/Makefile libgbinder-1.1.54/unit/unit_remote_request/unit_remote_request.c000066400000000000000000000346601526127616700252600ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_buffer_p.h" #include "gbinder_config.h" #include "gbinder_driver.h" #include "gbinder_reader.h" #include "gbinder_remote_request_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_local_request_p.h" #include "gbinder_object_converter.h" #include "gbinder_output_data.h" #include "gbinder_io.h" #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-remote-request-XXXXXX"; #define STRICT_MODE_PENALTY_GATHER (0x40 << 16) #define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER) #define TEST_RPC_IFACE "foo" #define TEST_RPC_HEADER \ TEST_INT32_BYTES(BINDER_RPC_FLAGS), \ TEST_INT32_BYTES(3), \ TEST_INT16_BYTES('f'), TEST_INT16_BYTES('o'), \ TEST_INT16_BYTES('o'), 0x00, 0x00 #define HIDL_RPC_HEADER \ 'f', 'o', 'o', 0x00 /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderReader reader; g_assert(!gbinder_remote_request_ref(NULL)); gbinder_remote_request_unref(NULL); gbinder_remote_request_init_reader(NULL, &reader); gbinder_remote_request_block(NULL); gbinder_remote_request_complete(NULL, NULL, 0); g_assert(gbinder_reader_at_end(&reader)); g_assert(!gbinder_remote_request_interface(NULL)); g_assert(!gbinder_remote_request_copy_to_local(NULL)); g_assert(!gbinder_remote_request_convert_to_local(NULL, NULL)); g_assert(gbinder_remote_request_sender_pid(NULL) == (pid_t)(-1)); g_assert(gbinder_remote_request_sender_euid(NULL) == (uid_t)(-1)); g_assert(!gbinder_remote_request_read_int32(NULL, NULL)); g_assert(!gbinder_remote_request_read_uint32(NULL, NULL)); g_assert(!gbinder_remote_request_read_int64(NULL, NULL)); g_assert(!gbinder_remote_request_read_uint64(NULL, NULL)); g_assert(!gbinder_remote_request_read_string8(NULL)); g_assert(!gbinder_remote_request_read_string16(NULL)); g_assert(!gbinder_remote_request_read_object(NULL)); g_assert(!gbinder_object_converter_handle_to_local(NULL, 0, NULL, NULL)); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { GBinderReader reader; GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(NULL), 0, 0); /* These two calls are wrong but won't cause problems: */ gbinder_remote_request_block(req); gbinder_remote_request_complete(req, NULL, 0); gbinder_remote_request_init_reader(req, &reader); g_assert(gbinder_reader_at_end(&reader)); g_assert(!gbinder_remote_request_interface(req)); g_assert(gbinder_remote_request_ref(req) == req); g_assert(!gbinder_remote_request_read_object(req)); gbinder_remote_request_unref(req); gbinder_remote_request_unref(req); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { static const guint8 req_data [] = { TEST_RPC_HEADER, TEST_INT32_BYTES(42) }; guint32 out1 = 0; gint32 out2 = 0; const char* dev = GBINDER_DEFAULT_BINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE)); g_assert(gbinder_remote_request_read_uint32(req, &out1)); g_assert(gbinder_remote_request_read_int32(req, &out2)); g_assert(out1 == 42); g_assert(out2 == 42u); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { static const guint8 req_data [] = { TEST_RPC_HEADER, TEST_INT64_BYTES(42) }; guint64 out1 = 0; gint64 out2 = 0; const char* dev = GBINDER_DEFAULT_BINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE)); g_assert(gbinder_remote_request_read_uint64(req, &out1)); g_assert(gbinder_remote_request_read_int64(req, &out2)); g_assert(out1 == 42); g_assert(out2 == 42u); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * string8 *==========================================================================*/ static void test_string8( void) { static const guint8 req_data [] = { TEST_RPC_HEADER, 'b', 'a', 'r', 0x00 }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE)); g_assert(!g_strcmp0(gbinder_remote_request_read_string8(req), "bar")); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * string16 *==========================================================================*/ static void test_string16( void) { static const guint8 req_data [] = { TEST_RPC_HEADER, TEST_INT32_BYTES(3), TEST_INT16_BYTES('b'), TEST_INT16_BYTES('a'), TEST_INT16_BYTES('r'), 0x00, 0x00 }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); char* str; gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, g_memdup(req_data, sizeof(req_data)), sizeof(req_data), NULL)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE)); str = gbinder_remote_request_read_string16(req); g_assert(!g_strcmp0(str, "bar")); g_free(str); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); } /*==========================================================================* * to_local *==========================================================================*/ static GBinderLocalObject* test_to_local_convert_none( GBinderObjectConverter* convert, guint32 handle, const GBinderRpcProtocol* protocol, const void* extra) { return NULL; } static void test_to_local( void) { static const guint8 request_data [] = { TEST_RPC_HEADER, /* 32-bit integer */ TEST_INT32_BYTES(42), /* 64-bit NULL flat_binder_object */ TEST_INT32_BYTES(BINDER_TYPE_BINDER), /* hdr.type */ TEST_INT32_BYTES(0x17f), /* flags */ TEST_INT64_BYTES(0), /* handle */ TEST_INT64_BYTES(0) /* cookie */ }; static const guint8 request_data_hidl [] = { HIDL_RPC_HEADER, /* 32-bit integer */ TEST_INT32_BYTES(42), /* 64-bit NULL flat_binder_object */ TEST_INT32_BYTES(BINDER_TYPE_BINDER), /* hdr.type */ TEST_INT32_BYTES(0x17f), /* flags */ TEST_INT64_BYTES(0), /* handle */ TEST_INT64_BYTES(0) /* cookie */ }; static const GBinderObjectConverterFunctions convert_f = { .handle_to_local = test_to_local_convert_none }; const char* dev = GBINDER_DEFAULT_BINDER; const char* dev2 = GBINDER_DEFAULT_HWBINDER; GBinderDriver* driver = gbinder_driver_new(dev, NULL); GBinderDriver* driver2 = gbinder_driver_new(dev2, NULL); GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); GBinderObjectConverter convert; GBinderLocalRequest* req2; GBinderOutputData* data; const GByteArray* bytes; GUtilIntArray* offsets; guint8* req_data = g_memdup(request_data, sizeof(request_data)); void** objects = g_new0(void*, 2); /* Skip the 32-bit integer */ objects[0] = req_data + 4; gbinder_remote_request_set_data(req, GBINDER_FIRST_CALL_TRANSACTION, gbinder_buffer_new(driver, req_data, sizeof(request_data), objects)); g_assert(!g_strcmp0(gbinder_remote_request_interface(req), TEST_RPC_IFACE)); /* Convert to GBinderLocalRequest */ req2 = gbinder_remote_request_copy_to_local(req); data = gbinder_local_request_data(req2); offsets = gbinder_output_data_offsets(data); bytes = data->bytes; g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 4); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(bytes->len == sizeof(request_data)); g_assert(!memcmp(request_data, bytes->data, bytes->len)); gbinder_local_request_unref(req2); memset(&convert, 0, sizeof(convert)); convert.f = &convert_f; convert.io = gbinder_driver_io(driver); convert.protocol = gbinder_driver_protocol(driver); /* The same with gbinder_remote_request_translate_to_local() */ req2 = gbinder_remote_request_convert_to_local(req, &convert); data = gbinder_local_request_data(req2); offsets = gbinder_output_data_offsets(data); bytes = data->bytes; g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 4); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(bytes->len == sizeof(request_data)); g_assert(!memcmp(request_data, bytes->data, bytes->len)); gbinder_local_request_unref(req2); memset(&convert, 0, sizeof(convert)); convert.f = &convert_f; convert.io = gbinder_driver_io(driver2); convert.protocol = gbinder_driver_protocol(driver2); /* Different driver actually requires translation */ req2 = gbinder_remote_request_convert_to_local(req, &convert); data = gbinder_local_request_data(req2); offsets = gbinder_output_data_offsets(data); bytes = data->bytes; g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 4); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(bytes->len == sizeof(request_data_hidl)); g_assert(!memcmp(request_data_hidl, bytes->data, bytes->len)); gbinder_local_request_unref(req2); gbinder_remote_request_unref(req); req = gbinder_remote_request_new(NULL, gbinder_rpc_protocol_for_device(dev), 0, 0); req2 = gbinder_remote_request_convert_to_local(req, &convert); data = gbinder_local_request_data(req2); bytes = data->bytes; g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(!bytes->len); gbinder_local_request_unref(req2); gbinder_remote_request_unref(req); gbinder_driver_unref(driver); gbinder_driver_unref(driver2); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/remote_request/" int main(int argc, char* argv[]) { TestConfig test_config; int result; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_PREFIX "null", test_null); g_test_add_func(TEST_PREFIX "basic", test_basic); g_test_add_func(TEST_PREFIX "int32", test_int32); g_test_add_func(TEST_PREFIX "int64", test_int64); g_test_add_func(TEST_PREFIX "string8", test_string8); g_test_add_func(TEST_PREFIX "string16", test_string16); g_test_add_func(TEST_PREFIX "to_local", test_to_local); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&test_config); return result; } /* * Remote Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager/000077500000000000000000000000001526127616700207315ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager/Makefile000066400000000000000000000001261526127616700223700ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager/unit_servicemanager.c000066400000000000000000001126561526127616700251420ustar00rootroot00000000000000/* * Copyright (C) 2018-2024 Slava Monich * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_client_p.h" #include "gbinder_config.h" #include "gbinder_remote_object_p.h" #include "gbinder_ipc.h" #include "gbinder_local_object_p.h" #include "gbinder_servicemanager_p.h" #include "gbinder_object_registry.h" #include #include #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager-XXXXXX"; static void test_get_service_func( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(FALSE); } static void test_add_service_func( GBinderServiceManager* sm, int status, void* user_data) { g_assert(FALSE); } static void test_registration_func_inc( GBinderServiceManager* sm, const char* name, void* user_data) { int* count = user_data; (*count)++; } static GBinderLocalReply* test_transact_func( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status, void* user_data) { return NULL; } static void test_inc( GBinderServiceManager* sm, void* user_data) { (*((int*)user_data))++; } static void test_reg_inc( GBinderServiceManager* sm, const char* name, void* user_data) { GVERBOSE_("\"%s\"", name); (*((int*)user_data))++; } static void test_quit( GBinderServiceManager* sm, void* user_data) { test_quit_later((GMainLoop*)user_data); } static void test_setup_ping( GBinderIpc* ipc) { const int fd = gbinder_driver_fd(ipc->driver); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); } /*==========================================================================* * TestServiceManager *==========================================================================*/ typedef GBinderServiceManagerClass TestServiceManagerClass; typedef struct test_servicemanager { GBinderServiceManager manager; GBinderRemoteObject* remote; char** services; gboolean reject_name; } TestServiceManager; #define TEST_SERVICEMANAGER(obj) \ G_CAST(obj, TestServiceManager, manager.parent) #define TEST_SERVICEMANAGER2(obj, type) \ G_TYPE_CHECK_INSTANCE_CAST((obj), (type), TestServiceManager) static char** test_servicemanager_list( GBinderServiceManager* sm, const GBinderIpcSyncApi* api) { TestServiceManager* self = TEST_SERVICEMANAGER(sm); return g_strdupv(self->services); } static GBinderRemoteObject* test_servicemanager_get_service( GBinderServiceManager* sm, const char* name, int* status, const GBinderIpcSyncApi* api) { TestServiceManager* self = TEST_SERVICEMANAGER(sm); if (gutil_strv_contains(self->services, name)) { if (!self->remote) { self->remote = gbinder_object_registry_get_remote (gbinder_ipc_object_registry(gbinder_client_ipc(sm->client)), 1, TRUE); } *status = GBINDER_STATUS_OK; return gbinder_remote_object_ref(self->remote); } else { *status = (-ENOENT); return NULL; } } static int test_servicemanager_add_service( GBinderServiceManager* sm, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api) { TestServiceManager* self = TEST_SERVICEMANAGER(sm); if (!gutil_strv_contains(self->services, name)) { self->services = gutil_strv_add(self->services, name); } return GBINDER_STATUS_OK; } /*==========================================================================* * TestHwServiceManager *==========================================================================*/ typedef TestServiceManagerClass TestHwServiceManagerClass; typedef TestServiceManager TestHwServiceManager; G_DEFINE_TYPE(TestHwServiceManager, test_hwservicemanager, GBINDER_TYPE_SERVICEMANAGER) #define TEST_HWSERVICEMANAGER_IFACE "android.hidl.manager@1.0::IServiceManager" #define TEST_TYPE_HWSERVICEMANAGER (test_hwservicemanager_get_type()) #define TEST_IS_HWSERVICEMANAGER(obj) \ G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_HWSERVICEMANAGER) #define TEST_HWSERVICEMANAGER(obj) \ TEST_SERVICEMANAGER2(obj, TEST_TYPE_HWSERVICEMANAGER) static GBINDER_SERVICEMANAGER_NAME_CHECK test_hwservicemanager_check_name( GBinderServiceManager* sm, const char* name) { TestHwServiceManager* self = TEST_HWSERVICEMANAGER(sm); return (!name || self->reject_name) ? GBINDER_SERVICEMANAGER_NAME_INVALID : GBINDER_SERVICEMANAGER_NAME_NORMALIZE; } static char* test_hwservicemanager_normalize_name( GBinderServiceManager* self, const char* name) { return g_strdup(name); } static gboolean test_hwservicemanager_watch( GBinderServiceManager* manager, const char* name) { return TRUE; } static void test_hwservicemanager_unwatch( GBinderServiceManager* manager, const char* name) { } static void test_hwservicemanager_init( TestHwServiceManager* self) { } static void test_hwservicemanager_finalize( GObject* object) { TestHwServiceManager* self = TEST_HWSERVICEMANAGER(object); gbinder_remote_object_unref(self->remote); g_strfreev(self->services); G_OBJECT_CLASS(test_hwservicemanager_parent_class)->finalize(object); } static void test_hwservicemanager_class_init( TestHwServiceManagerClass* klass) { klass->iface = TEST_HWSERVICEMANAGER_IFACE; klass->default_device = GBINDER_DEFAULT_HWBINDER; klass->list = test_servicemanager_list; klass->get_service = test_servicemanager_get_service; klass->add_service = test_servicemanager_add_service; klass->check_name = test_hwservicemanager_check_name; klass->normalize_name = test_hwservicemanager_normalize_name; klass->watch = test_hwservicemanager_watch; klass->unwatch = test_hwservicemanager_unwatch; G_OBJECT_CLASS(klass)->finalize = test_hwservicemanager_finalize; } GType gbinder_servicemanager_hidl_get_type() { return TEST_TYPE_HWSERVICEMANAGER; } /*==========================================================================* * TestDefServiceManager *==========================================================================*/ typedef TestServiceManagerClass TestDefServiceManagerClass; typedef TestServiceManager TestDefServiceManager; G_DEFINE_TYPE(TestDefServiceManager, test_defservicemanager, GBINDER_TYPE_SERVICEMANAGER) #define TEST_DEFSERVICEMANAGER_IFACE "android.os.IServiceManager" #define TEST_TYPE_DEFSERVICEMANAGER (test_defservicemanager_get_type()) #define TEST_IS_DEFSERVICEMANAGER(obj) \ G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_DEFSERVICEMANAGER) #define TEST_DEFSERVICEMANAGER(obj) \ TEST_SERVICEMANAGER2(obj, TEST_TYPE_DEFSERVICEMANAGER) static GBINDER_SERVICEMANAGER_NAME_CHECK test_defservicemanager_check_name( GBinderServiceManager* sm, const char* name) { TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(sm); return (!name || self->reject_name) ? GBINDER_SERVICEMANAGER_NAME_INVALID : GBINDER_SERVICEMANAGER_NAME_OK; } static gboolean test_defservicemanager_watch( GBinderServiceManager* manager, const char* name) { return FALSE; } static void test_defservicemanager_init( TestDefServiceManager* self) { } static void test_defservicemanager_finalize( GObject* object) { TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(object); gbinder_remote_object_unref(self->remote); g_strfreev(self->services); G_OBJECT_CLASS(test_defservicemanager_parent_class)->finalize(object); } static void test_defservicemanager_class_init( TestDefServiceManagerClass* klass) { klass->iface = TEST_DEFSERVICEMANAGER_IFACE; klass->default_device = GBINDER_DEFAULT_BINDER; klass->list = test_servicemanager_list; klass->get_service = test_servicemanager_get_service; klass->add_service = test_servicemanager_add_service; klass->check_name = test_defservicemanager_check_name; klass->watch = test_defservicemanager_watch; G_OBJECT_CLASS(klass)->finalize = test_defservicemanager_finalize; } GType gbinder_servicemanager_aidl_get_type() { return TEST_TYPE_DEFSERVICEMANAGER; } GType gbinder_servicemanager_aidl2_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl3_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl5_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl5 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { g_assert(!gbinder_servicemanager_new(NULL)); g_assert(!gbinder_servicemanager_new_with_type(0, NULL, NULL)); g_assert(!gbinder_servicemanager_new_local_object(NULL, NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_ref(NULL)); g_assert(!gbinder_servicemanager_device(NULL)); g_assert(!gbinder_servicemanager_is_present(NULL)); g_assert(!gbinder_servicemanager_wait(NULL, 0)); g_assert(!gbinder_servicemanager_list(NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_list_sync(NULL)); g_assert(!gbinder_servicemanager_get_service(NULL, NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_get_service_sync(NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_add_service(NULL, NULL, NULL, NULL, NULL)); g_assert(gbinder_servicemanager_add_service_sync(NULL, NULL, NULL) == (-EINVAL)); g_assert(!gbinder_servicemanager_add_presence_handler(NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_add_registration_handler(NULL, NULL, NULL, NULL)); gbinder_servicemanager_remove_handler(NULL, 0); gbinder_servicemanager_remove_handlers(NULL, NULL, 0); gbinder_servicemanager_cancel(NULL, 0); gbinder_servicemanager_unref(NULL); } /*==========================================================================* * invalid *==========================================================================*/ static void test_invalid( void) { int status = 0; const char* dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* ipc; GBinderServiceManager* sm; TestConfig config; gulong id = 0; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); g_assert(!gbinder_servicemanager_new2(GBINDER_DEFAULT_HWBINDER, "a", NULL)); sm = gbinder_servicemanager_new(dev); g_assert(!gbinder_servicemanager_new_with_type(GBINDER_TYPE_LOCAL_OBJECT, NULL, NULL)); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); g_assert(!gbinder_servicemanager_list(sm, NULL, NULL)); g_assert(!gbinder_servicemanager_get_service(sm, "foo", NULL, NULL)); g_assert(!gbinder_servicemanager_get_service(sm, NULL, test_get_service_func, NULL)); g_assert(!gbinder_servicemanager_get_service_sync(sm, NULL, NULL)); g_assert(!gbinder_servicemanager_get_service_sync(sm, NULL, &status)); g_assert(status == (-EINVAL)); g_assert(!gbinder_servicemanager_add_service(sm, "foo", NULL, NULL, NULL)); g_assert(!gbinder_servicemanager_add_service(sm, NULL, NULL, test_add_service_func, NULL)); g_assert(gbinder_servicemanager_add_service_sync(sm, NULL, NULL) == (-EINVAL)); g_assert(gbinder_servicemanager_add_service_sync(sm, "foo", NULL) == (-EINVAL)); g_assert(!gbinder_servicemanager_add_presence_handler(sm, NULL, NULL)); g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL, NULL, NULL)); gbinder_servicemanager_cancel(sm, 0); gbinder_servicemanager_remove_handler(sm, 0); gbinder_servicemanager_remove_handlers(sm, NULL, 0); gbinder_servicemanager_remove_handlers(sm, &id, 0); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* ipc; GBinderServiceManager* sm; GBinderLocalObject* obj; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); g_assert(sm); obj = gbinder_servicemanager_new_local_object(sm, "foo.bar", test_transact_func, NULL); g_assert(obj); g_assert_cmpstr(gbinder_servicemanager_device(sm), == ,dev); gbinder_local_object_unref(obj); g_assert(gbinder_servicemanager_ref(sm) == sm); gbinder_servicemanager_unref(sm); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * legacy *==========================================================================*/ static void test_legacy( void) { const char* otherdev = "/dev/otherbinder"; const char* dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* ipc; GBinderServiceManager* sm; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_hwservicemanager_new(dev); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); test_setup_ping(ipc); sm = gbinder_defaultservicemanager_new(dev); g_assert(TEST_IS_DEFSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* Legacy default */ ipc = gbinder_ipc_new(otherdev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(otherdev); g_assert(TEST_IS_DEFSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); gbinder_servicemanager_exit(); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * config *==========================================================================*/ static void test_config( void) { GBinderIpc* ipc; GBinderServiceManager* sm; const char* strange_name = "/dev/notbinder"; const char* legacy_name = "/dev/legacybinder"; TestConfig test; char* file; static const char config[] = "[ServiceManager]\n" "Default = hidl\n" "/dev/binder = hidl\n" /* Redefined name for /dev/binder */ "/dev/hwbinder = foo\n" /* Invalid name */ "/dev/legacybinder = aidl\n"; test_config_init(&test, TMP_DIR_TEMPLATE); file = g_build_filename(test.config_dir, "test.conf", NULL); /* Write the config file */ g_assert(g_file_set_contents(file, config, -1, NULL)); GDEBUG("Config file %s", file); gbinder_config_file = file; /* Unknown device instantiates the default */ ipc = gbinder_ipc_new(strange_name, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(strange_name); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* This one was redefined */ ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(GBINDER_DEFAULT_BINDER); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* This one was not (since name was invalid) */ ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(GBINDER_DEFAULT_HWBINDER); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* This one points to legacy manager */ ipc = gbinder_ipc_new(legacy_name, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(legacy_name); g_assert(TEST_IS_DEFSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* Overwrite the legacy manager with "hidl" */ ipc = gbinder_ipc_new(legacy_name, "hidl"); test_setup_ping(ipc); sm = gbinder_servicemanager_new2(legacy_name, "hidl", "hidl"); g_assert(TEST_IS_HWSERVICEMANAGER(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); /* Clear the state */ gbinder_servicemanager_exit(); test_binder_exit_wait(&test_opt, NULL); remove(file); g_free(file); test_config_cleanup(&test); } /*==========================================================================* * not_present *==========================================================================*/ static void test_not_present( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* ipc; GBinderServiceManager* sm; TestConfig config; int fd; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); /* This makes presence detection PING fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(!gbinder_servicemanager_is_present(sm)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * wait *==========================================================================*/ static void test_wait( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; const glong forever = (test_opt.flags & TEST_FLAG_DEBUG) ? (TEST_TIMEOUT_SEC * 1000) : -1; GBinderIpc* ipc; GBinderServiceManager* sm; gulong id; int fd, count = 0; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); /* This makes presence detection PING fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(!gbinder_servicemanager_is_present(sm)); /* Register the listener */ id = gbinder_servicemanager_add_presence_handler(sm, test_inc, &count); g_assert(id); /* Make this wait fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); g_assert(!gbinder_servicemanager_wait(sm, 0)); /* This makes presence detection PING succeed */ test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); g_assert(gbinder_servicemanager_wait(sm, forever)); /* The next check succeeds too (without any I/O ) */ g_assert(gbinder_servicemanager_is_present(sm)); g_assert(gbinder_servicemanager_wait(sm, 0)); /* The listener must have been invoked exactly once */ g_assert(count == 1); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * wait_long *==========================================================================*/ static void test_wait_long( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* ipc; GBinderServiceManager* sm; gulong id; int fd, count = 0; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); /* This makes presence detection PING fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(!gbinder_servicemanager_is_present(sm)); /* Register the listener */ id = gbinder_servicemanager_add_presence_handler(sm, test_inc, &count); g_assert(id); /* Make the first presence detection PING fail and second succeed */ test_binder_br_reply_status(fd, THIS_THREAD, -1); test_binder_br_reply_status(fd, TX_THREAD, -1); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_reply(fd, TX_THREAD, 0, 0, NULL); g_assert(gbinder_servicemanager_wait(sm, TEST_TIMEOUT_SEC * 1000)); /* The next check succeeds too (without any I/O ) */ g_assert(gbinder_servicemanager_is_present(sm)); g_assert(gbinder_servicemanager_wait(sm, 0)); /* The listener must have been invoked exactly once */ g_assert(count == 1); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * wait_async *==========================================================================*/ static void test_wait_async( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; gulong id[2]; int fd, count = 0; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); /* This makes presence detection PING fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(!gbinder_servicemanager_is_present(sm)); /* Register the listeners */ id[0] = gbinder_servicemanager_add_presence_handler(sm, test_inc, &count); id[1] = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); g_assert(id[0]); g_assert(id[1]); /* Make the first presence detection PING fail and second succeed */ test_binder_br_reply_status(fd, THIS_THREAD, -1); test_binder_br_transaction_complete(fd, TX_THREAD); test_binder_br_reply(fd, TX_THREAD, 0, 0, NULL); test_run(&test_opt, loop); /* The listener must have been invoked exactly once */ g_assert(count == 1); gbinder_servicemanager_remove_all_handlers(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } /*==========================================================================* * death *==========================================================================*/ static void test_death_run() { const char* dev = GBINDER_DEFAULT_HWBINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; gulong id[3]; int fd, count = 0, reg_count = 0; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(gbinder_servicemanager_is_present(sm)); /* Register the listeners */ id[0] = gbinder_servicemanager_add_presence_handler(sm, test_inc, &count); id[1] = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); id[2] = gbinder_servicemanager_add_registration_handler(sm, "foo", test_reg_inc, ®_count); g_assert(id[0]); g_assert(id[1]); g_assert(id[2]); /* Generate death notification */ test_binder_br_dead_binder(fd, ANY_THREAD, 0); test_run(&test_opt, loop); /* No registrations must have occured */ g_assert(!reg_count); /* The listener must have been invoked exactly once */ g_assert(count == 1); g_assert(!gbinder_servicemanager_is_present(sm)); gbinder_servicemanager_remove_all_handlers(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } static void test_death() { test_run_in_context(&test_opt, test_death_run); } /*==========================================================================* * reanimate *==========================================================================*/ static void test_reanimate_quit( GBinderServiceManager* sm, void* user_data) { if (gbinder_servicemanager_is_present(sm)) { GDEBUG("Service manager is back"); test_quit_later((GMainLoop*)user_data); } else { const int fd = gbinder_driver_fd(sm->client->remote->ipc->driver); /* Reanimate the object */ GDEBUG("Reanimating..."); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); } } static void test_reanimate( void) { const char* dev = GBINDER_DEFAULT_HWBINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; gulong id[3]; int fd, count = 0, reg_count = 0; TestConfig config; /* Create live service manager */ test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); fd = gbinder_driver_fd(ipc->driver); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); g_assert(sm); g_assert(gbinder_servicemanager_is_present(sm)); /* Register the listeners */ id[0] = gbinder_servicemanager_add_presence_handler(sm, test_inc, &count); id[1] = gbinder_servicemanager_add_presence_handler(sm, test_reanimate_quit, loop); id[2] = gbinder_servicemanager_add_registration_handler(sm, "foo", test_reg_inc, ®_count); g_assert(id[0]); g_assert(id[1]); g_assert(id[2]); /* Generate death notification */ test_binder_br_dead_binder(fd, ANY_THREAD, 0); test_run(&test_opt, loop); /* No registrations must have occured */ g_assert(!reg_count); /* Presence must have changed twice */ g_assert(count == 2); g_assert(gbinder_servicemanager_is_present(sm)); gbinder_servicemanager_remove_all_handlers(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } /*==========================================================================* * reuse *==========================================================================*/ static void test_reuse( void) { const char* binder_dev = GBINDER_DEFAULT_BINDER; const char* vndbinder_dev = "/dev/vpnbinder"; const char* hwbinder_dev = GBINDER_DEFAULT_HWBINDER; GBinderIpc* binder_ipc; GBinderIpc* vndbinder_ipc; GBinderIpc* hwbinder_ipc; GBinderServiceManager* m1; GBinderServiceManager* m2; GBinderServiceManager* vnd1; GBinderServiceManager* vnd2; GBinderServiceManager* hw1; GBinderServiceManager* hw2; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); binder_ipc = gbinder_ipc_new(binder_dev, NULL); vndbinder_ipc = gbinder_ipc_new(vndbinder_dev, NULL); hwbinder_ipc = gbinder_ipc_new(hwbinder_dev, NULL); test_setup_ping(binder_ipc); test_setup_ping(vndbinder_ipc); test_setup_ping(hwbinder_ipc); m1 = gbinder_servicemanager_new(binder_dev); m2 = gbinder_servicemanager_new(binder_dev); vnd1 = gbinder_servicemanager_new(vndbinder_dev); vnd2 = gbinder_servicemanager_new(vndbinder_dev); hw1 = gbinder_servicemanager_new(hwbinder_dev); hw2 = gbinder_servicemanager_new(hwbinder_dev); g_assert(m1); g_assert(m1 == m2); g_assert(vnd1); g_assert(vnd1 == vnd2); g_assert(vnd1 != m1); g_assert(hw1); g_assert(hw1 == hw2); g_assert(hw1 != m1); g_assert(hw1 != vnd1); gbinder_servicemanager_unref(m1); gbinder_servicemanager_unref(m2); gbinder_servicemanager_unref(vnd1); gbinder_servicemanager_unref(vnd2); gbinder_servicemanager_unref(hw1); gbinder_servicemanager_unref(hw2); gbinder_ipc_unref(binder_ipc); gbinder_ipc_unref(vndbinder_ipc); gbinder_ipc_unref(hwbinder_ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } /*==========================================================================* * notify *==========================================================================*/ static void test_notify_type( GType t, const char* dev) { GBinderIpc* ipc; GBinderServiceManager* sm; TestHwServiceManager* test; const char* name = "foo"; int count = 0; gulong id1, id2; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new_with_type(t, NULL, NULL); test = TEST_SERVICEMANAGER2(sm, t); id1 = gbinder_servicemanager_add_registration_handler(sm, name, test_registration_func_inc, &count); id2 = gbinder_servicemanager_add_registration_handler(sm, name, test_registration_func_inc, &count); g_assert(id1 && id2); test->services = gutil_strv_add(test->services, name); gbinder_servicemanager_service_registered(sm, name); g_assert(count == 2); count = 0; /* Nothing is going to happen if the name get rejected by the class */ test->reject_name = TRUE; g_assert(!gbinder_servicemanager_add_registration_handler(sm, name, test_registration_func_inc, &count)); gbinder_servicemanager_service_registered(sm, name); g_assert(!count); gbinder_servicemanager_remove_handler(sm, id1); gbinder_servicemanager_remove_handler(sm, id2); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); test_config_cleanup(&config); } static void test_notify( void) { test_notify_type(TEST_TYPE_HWSERVICEMANAGER, GBINDER_DEFAULT_HWBINDER); test_notify_type(TEST_TYPE_DEFSERVICEMANAGER, GBINDER_DEFAULT_BINDER); } /*==========================================================================* * list *==========================================================================*/ static gboolean test_list_func( GBinderServiceManager* sm, char** services, void* user_data) { TestHwServiceManager* test = TEST_SERVICEMANAGER(sm); g_assert(gutil_strv_equal(test->services, services)); test_quit_later((GMainLoop*)user_data); return FALSE; } static void test_list( void) { const char* dev = GBINDER_DEFAULT_BINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; TestHwServiceManager* test; char** list; gulong id; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); test = TEST_SERVICEMANAGER(sm); test->services = gutil_strv_add(test->services, "foo"); list = gbinder_servicemanager_list_sync(sm); g_assert(gutil_strv_equal(test->services, list)); g_strfreev(list); id = gbinder_servicemanager_list(sm, test_list_func, loop); g_assert(id); test_run(&test_opt, loop); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } /*==========================================================================* * get *==========================================================================*/ static void test_get_func( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(status == GBINDER_STATUS_OK); g_assert(obj); test_quit_later((GMainLoop*)user_data); } static void test_get( void) { const char* dev = GBINDER_DEFAULT_BINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; TestHwServiceManager* test; int status = -1; GBinderLocalObject* obj; gulong id; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); test = TEST_SERVICEMANAGER(sm); obj = gbinder_servicemanager_new_local_object(sm, "foo.bar", test_transact_func, NULL); /* Add a service */ g_assert(obj); g_assert(gbinder_servicemanager_add_service_sync(sm, "foo", obj) == GBINDER_STATUS_OK); gbinder_local_object_unref(obj); g_assert(gutil_strv_contains(test->services, "foo")); /* And get it back */ g_assert(gbinder_servicemanager_get_service_sync(sm, "foo", &status)); g_assert(status == GBINDER_STATUS_OK); /* Wrong name */ g_assert(!gbinder_servicemanager_get_service_sync(sm, "bar", &status)); g_assert(status == (-ENOENT)); /* Get it asynchronously */ id = gbinder_servicemanager_get_service(sm, "foo", test_get_func, loop); g_assert(id); test_run(&test_opt, loop); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } /*==========================================================================* * add *==========================================================================*/ static void test_add_func( GBinderServiceManager* sm, int status, void* user_data) { g_assert(status == GBINDER_STATUS_OK); test_quit_later((GMainLoop*)user_data); } static void test_add( void) { const char* dev = GBINDER_DEFAULT_BINDER; GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderIpc* ipc; GBinderServiceManager* sm; TestHwServiceManager* test; GBinderLocalObject* obj; gulong id; TestConfig config; test_config_init(&config, TMP_DIR_TEMPLATE); ipc = gbinder_ipc_new(dev, NULL); test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); test = TEST_SERVICEMANAGER(sm); obj = gbinder_servicemanager_new_local_object(sm, "foo.bar", test_transact_func, NULL); id = gbinder_servicemanager_add_service(sm, "foo", obj, test_add_func, loop); g_assert(id); test_run(&test_opt, loop); g_assert(gutil_strv_contains(test->services, "foo")); gbinder_local_object_unref(obj); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); test_config_cleanup(&config); g_main_loop_unref(loop); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("invalid"), test_invalid); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("legacy"), test_legacy); g_test_add_func(TEST_("config"), test_config); g_test_add_func(TEST_("not_present"), test_not_present); g_test_add_func(TEST_("wait"), test_wait); g_test_add_func(TEST_("wait_long"), test_wait_long); g_test_add_func(TEST_("wait_async"), test_wait_async); g_test_add_func(TEST_("death"), test_death); g_test_add_func(TEST_("reanimate"), test_reanimate); g_test_add_func(TEST_("reuse"), test_reuse); g_test_add_func(TEST_("notify"), test_notify); g_test_add_func(TEST_("list"), test_list); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("add"), test_add); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_aidl/000077500000000000000000000000001526127616700217225ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_aidl/Makefile000066400000000000000000000001331526127616700233570ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_aidl include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_aidl/unit_servicemanager_aidl.c000066400000000000000000000571421526127616700271220ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_client_p.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_reader.h" #include "gbinder_writer.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_local_request.h" #include "gbinder_remote_request.h" #include "gbinder_remote_object_p.h" #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager_aidl-XXXXXX"; GType gbinder_servicemanager_hidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl2_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl3_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl5_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl5 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * Test service manager *==========================================================================*/ #define SVCMGR_HANDLE (0) static const char SVCMGR_IFACE[] = "android.os.IServiceManager"; static const char CALLBACK_IFACE[] = "android.os.IServiceCallback"; enum servicemanager_aidl_tx { GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION, REGISTER_FOR_NOTIFICATIONS_TRANSACTION, UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION }; enum servicemanager_aidl_callbacl_tx { ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION }; const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL }; typedef GBinderLocalObjectClass ServiceManagerAidlClass; typedef struct service_manager_aidl { GBinderLocalObject parent; gboolean notify; GHashTable* objects; GPtrArray* watches; GMutex mutex; } ServiceManagerAidl; typedef struct service_manager_aidl_watch { char* name; GBinderClient* watcher; } ServiceManagerAidlWatch; #define SERVICE_MANAGER_AIDL_TYPE (service_manager_aidl_get_type()) #define SERVICE_MANAGER_AIDL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ SERVICE_MANAGER_AIDL_TYPE, ServiceManagerAidl)) G_DEFINE_TYPE(ServiceManagerAidl, service_manager_aidl, \ GBINDER_TYPE_LOCAL_OBJECT) static void test_servicemanager_aidl_watch_destroy( gpointer data) { ServiceManagerAidlWatch* watch = data; gbinder_client_unref(watch->watcher); g_free(watch->name); g_free(watch); } static void test_servicemanager_aidl_watch_notify( const ServiceManagerAidlWatch* watch, GBinderRemoteObject* obj) { GBinderLocalRequest* notify = gbinder_client_new_request(watch->watcher); GBinderWriter writer; /* * IServiceCallback.aidl: * void onRegistration(@utf8InCpp String name, IBinder binder); */ gbinder_local_request_init_writer(notify, &writer); gbinder_writer_append_string16(&writer, watch->name); gbinder_writer_append_remote_object(&writer, obj); gbinder_client_transact(watch->watcher, ON_REGISTRATION_TRANSACTION, GBINDER_TX_FLAG_ONEWAY, notify, NULL, NULL, NULL); gbinder_local_request_unref(notify); } static GBinderLocalReply* servicemanager_aidl_handler( ServiceManagerAidl* self, GBinderRemoteRequest* req, guint code, int* status) { GBinderLocalObject* obj = &self->parent; GBinderLocalReply* reply = NULL; GBinderReader reader; GBinderRemoteObject* remote_obj; guint32 num; char* str; GDEBUG("%s %u", gbinder_remote_request_interface(req), code); gbinder_remote_request_init_reader(req, &reader); *status = -1; /* Lock */ g_mutex_lock(&self->mutex); switch (code) { case GET_SERVICE_TRANSACTION: case CHECK_SERVICE_TRANSACTION: str = gbinder_reader_read_string16(&reader); if (str) { reply = gbinder_local_object_new_reply(obj); remote_obj = g_hash_table_lookup(self->objects, str); if (remote_obj) { GDEBUG("Found name '%s' => %p", str, remote_obj); gbinder_local_reply_append_remote_object(reply, remote_obj); } else { GDEBUG("Name '%s' not found", str); gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK); } g_free(str); } break; case ADD_SERVICE_TRANSACTION: str = gbinder_reader_read_string16(&reader); remote_obj = gbinder_reader_read_object(&reader); if (str && remote_obj && gbinder_reader_read_uint32(&reader, &num)) { const GPtrArray* watches = self->watches; guint i; GDEBUG("Adding '%s'", str); g_hash_table_replace(self->objects, str, remote_obj); reply = gbinder_local_object_new_reply(obj); *status = GBINDER_STATUS_OK; for (i = 0; i < watches->len; i++) { const ServiceManagerAidlWatch* watch = watches->pdata[i]; if (!strcmp(str, watch->name)) { test_servicemanager_aidl_watch_notify(watch, remote_obj); } } remote_obj = NULL; str = NULL; } g_free(str); gbinder_remote_object_unref(remote_obj); break; case LIST_SERVICES_TRANSACTION: if (gbinder_reader_read_uint32(&reader, &num)) { if (num < g_hash_table_size(self->objects)) { GList* keys = g_hash_table_get_keys(self->objects); GList* l = g_list_nth(keys, num); reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_append_string16(reply, l->data); g_list_free(keys); *status = GBINDER_STATUS_OK; } else { GDEBUG("Index %u out of bounds", num); } } break; case REGISTER_FOR_NOTIFICATIONS_TRANSACTION: /* * void registerForNotifications(@utf8InCpp String name, * IServiceCallback callback); */ if (self->notify) { char* name = gbinder_reader_read_string16(&reader); GBinderRemoteObject* obj = gbinder_reader_read_object(&reader); if (name && obj) { ServiceManagerAidlWatch* watch = g_new(ServiceManagerAidlWatch, 1); GDEBUG("Registering watcher %u for '%s'", (guint) obj->handle, name); watch->watcher = gbinder_client_new(obj, CALLBACK_IFACE); watch->name = name; name = NULL; /* We have stolen it */ g_ptr_array_add(self->watches, watch); *status = GBINDER_STATUS_OK; } gbinder_remote_object_unref(obj); g_free(name); } else { GDEBUG("registerForNotifications not supported"); } break; case UNREGISTER_FOR_NOTIFICATIONS_TRANSACTION: /* * void unregisterForNotifications(@utf8InCpp String name, * IServiceCallback callback); */ if (self->notify) { char* name = gbinder_reader_read_string16(&reader); GBinderRemoteObject* obj = gbinder_reader_read_object(&reader); if (name && obj) { GPtrArray* watches = self->watches; guint i; for (i = 0; i < watches->len; i++) { ServiceManagerAidlWatch* watch = watches->pdata[i]; if (watch->watcher->remote->handle == obj->handle && !strcmp(name, watch->name)) { GDEBUG("Unregistering watcher %u for '%s'", (guint) obj->handle, name); g_ptr_array_remove_index(watches, i); *status = GBINDER_STATUS_OK; break; } } } gbinder_remote_object_unref(obj); g_free(name); } else { GDEBUG("registerForNotifications not supported"); } break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); /* Unlock */ return reply; } static GBinderLocalReply* servicemanager_aidl_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return !g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE) ? servicemanager_aidl_handler(SERVICE_MANAGER_AIDL(obj), req, code, status) : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)-> handle_looper_transaction(obj, req, code, flags, status); } static GBINDER_LOCAL_TRANSACTION_SUPPORT servicemanager_aidl_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Handle servicemanager transactions on the looper thread */ return !g_strcmp0(iface, SVCMGR_IFACE) ? GBINDER_LOCAL_TRANSACTION_LOOPER : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)-> can_handle_transaction(self, iface, code); } static void service_manager_aidl_finalize( GObject* object) { ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); g_ptr_array_free(self->watches, TRUE); G_OBJECT_CLASS(service_manager_aidl_parent_class)->finalize(object); } static void service_manager_aidl_init( ServiceManagerAidl* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); self->watches = g_ptr_array_new_with_free_func((GDestroyNotify) test_servicemanager_aidl_watch_destroy); } static void service_manager_aidl_class_init( ServiceManagerAidlClass* klass) { G_OBJECT_CLASS(klass)->finalize = service_manager_aidl_finalize; klass->can_handle_transaction = servicemanager_aidl_can_handle_transaction; klass->handle_looper_transaction = servicemanager_aidl_handle_looper_transaction; } static ServiceManagerAidl* servicemanager_aidl_new2( const char* dev, gboolean notify) { ServiceManagerAidl* self = g_object_new(SERVICE_MANAGER_AIDL_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); self->notify = notify; gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces, NULL, NULL); test_binder_register_object(fd, obj, SVCMGR_HANDLE); gbinder_ipc_register_local_object(ipc, obj); gbinder_ipc_unref(ipc); return self; } static ServiceManagerAidl* servicemanager_aidl_new( const char* dev) { return servicemanager_aidl_new2(dev, FALSE); } /*==========================================================================* * get *==========================================================================*/ static void test_add_cb( GBinderServiceManager* sm, int status, void* user_data) { g_assert(status == GBINDER_STATUS_OK); if (user_data) { g_main_loop_quit(user_data); } } static void test_get_none_cb( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(!obj); g_assert(status == GBINDER_STATUS_OK); g_main_loop_quit(user_data); } static void test_get_cb( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(obj); g_assert(status == GBINDER_STATUS_OK); g_main_loop_quit(user_data); } static void test_get_run() { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); ServiceManagerAidl* smsvc = servicemanager_aidl_new(dev); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* name = "name"; GBinderServiceManager* sm; GMainLoop* loop = g_main_loop_new(NULL, FALSE); /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(dev); /* Query the object (it's not there yet) and wait for completion */ GDEBUG("Querying '%s'", name); g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb, loop)); test_run(&test_opt, loop); /* Register object and wait for completion */ GDEBUG("Registering object '%s' => %p", name, obj); g_assert(gbinder_servicemanager_add_service(sm, name, obj, test_add_cb, loop)); test_run(&test_opt, loop); g_assert_cmpuint(g_hash_table_size(smsvc->objects), == ,1); g_assert(g_hash_table_contains(smsvc->objects, name)); /* Query the object (this time it must be there) and wait for completion */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop)); test_run(&test_opt, loop); test_binder_unregister_objects(fd); gbinder_local_object_unref(obj); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(smsvc)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_get() { test_run_in_context(&test_opt, test_get_run); } /*==========================================================================* * list *==========================================================================*/ typedef struct test_list { char** list; GMainLoop* loop; } TestList; static gboolean test_list_cb( GBinderServiceManager* sm, char** services, void* user_data) { TestList* test = user_data; GDEBUG("Got %u name(s)", gutil_strv_length(services)); g_strfreev(test->list); test->list = services; g_main_loop_quit(test->loop); return TRUE; } static void test_list_run() { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); ServiceManagerAidl* smsvc = servicemanager_aidl_new(dev); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); const int fd = gbinder_driver_fd(ipc->driver); const char* name = "name"; GBinderServiceManager* sm; TestList test; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(dev); /* Request the list and wait for completion */ g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test)); test_run(&test_opt, test.loop); /* There's nothing there yet */ g_assert(test.list); g_assert(!test.list[0]); /* Register object and wait for completion */ g_assert(gbinder_servicemanager_add_service(sm, name, obj, test_add_cb, test.loop)); test_run(&test_opt, test.loop); /* Request the list again */ g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test)); test_run(&test_opt, test.loop); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(test.list), == ,1); g_assert_cmpstr(test.list[0], == ,name); test_binder_unregister_objects(fd); gbinder_local_object_unref(obj); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(smsvc)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, test.loop); g_strfreev(test.list); g_main_loop_unref(test.loop); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * notify *==========================================================================*/ typedef struct test_notify { const char* name; gulong watch_id; GBinderLocalObject* obj; GMainLoop* loop; } TestNotify; static gboolean test_notify_done_cb( GBinderServiceManager* sm, char** services, void* user_data) { TestNotify* test = user_data; g_assert_true(gutil_strv_contains(services, test->name)); gbinder_local_object_unref(test->obj); test->obj = NULL; g_main_loop_quit(test->loop); return FALSE; } static void test_notify_cb( GBinderServiceManager* sm, const char* name, void* user_data) { TestNotify* test = user_data; GDEBUG("'%s' is registered", name); g_assert_cmpuint(test->watch_id, != ,0); gbinder_servicemanager_remove_handler(sm, test->watch_id); test->watch_id = 0; g_assert(gbinder_servicemanager_list(sm, test_notify_done_cb, test)); } static void test_notify_run( gconstpointer param) { const char* dev = GBINDER_DEFAULT_BINDER; const gboolean aidl_notify = GPOINTER_TO_INT(param); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); ServiceManagerAidl* svc = servicemanager_aidl_new2(dev, aidl_notify); const int fd = gbinder_driver_fd(ipc->driver); TestNotify test; GBinderServiceManager* sm; memset(&test, 0, sizeof(test)); test.name = "test"; test.loop = g_main_loop_new(NULL, FALSE); test.obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); /* Set up binder simulator */ test_binder_register_object(fd, test.obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(dev); gbinder_ipc_set_max_threads(ipc, 1); /* Start watching */ test.watch_id = gbinder_servicemanager_add_registration_handler(sm, test.name, test_notify_cb, &test); g_assert_cmpuint(test.watch_id, != ,0); /* Register the object and wait for completion */ GDEBUG("Registering object '%s' => %p", test.name, test.obj); g_assert(gbinder_servicemanager_add_service(sm, test.name, test.obj, test_add_cb, NULL)); /* test_notify_cb will stop the loop */ test_run(&test_opt, test.loop); g_assert_cmpuint(test.watch_id, == ,0); g_assert_null(test.obj); test_binder_unregister_objects(fd); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(svc)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_notify( gconstpointer param) { test_run_in_context_param(&test_opt, test_notify_run, param); } /*==========================================================================* * notify2 *==========================================================================*/ static void test_notify2_cb( GBinderServiceManager* sm, const char* name, void* user_data) { g_assert(name); GDEBUG("'%s' is registered", name); g_main_loop_quit(user_data); } static void test_notify2_run() { /* This test relies on the polling notification mechanism */ const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); ServiceManagerAidl* smsvc = servicemanager_aidl_new(dev); GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); const int fd = gbinder_driver_fd(ipc->driver); GBinderServiceManager* sm; const char* name1 = "name1"; const char* name2 = "name2"; GMainLoop* loop = g_main_loop_new(NULL, FALSE); gulong id1, id2; /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(dev); gbinder_ipc_set_max_threads(ipc, 1); /* Register the object synchronously (twice)*/ GDEBUG("Registering object '%s' => %p", name1, obj); g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name1,obj),==,0); g_assert(gbinder_servicemanager_get_service_sync(sm, name1, NULL)); GDEBUG("Registering object '%s' => %p", name2, obj); g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name2,obj),==,0); g_assert(gbinder_servicemanager_get_service_sync(sm, name2, NULL)); /* Watch for the first name to create internal name watcher */ id1 = gbinder_servicemanager_add_registration_handler(sm, name1, test_notify2_cb, loop); g_assert(id1); test_run(&test_opt, loop); /* Now watch for the second name */ id2 = gbinder_servicemanager_add_registration_handler(sm, name2, test_notify2_cb, loop); g_assert(id2); test_run(&test_opt, loop); gbinder_servicemanager_remove_handler(sm, id1); gbinder_servicemanager_remove_handler(sm, id2); test_binder_unregister_objects(fd); gbinder_local_object_unref(obj); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(smsvc)); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_notify2() { test_run_in_context(&test_opt, test_notify2_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_aidl/" t int main(int argc, char* argv[]) { TestConfig config; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("list"), test_list); g_test_add_data_func(TEST_("notify/1"), GINT_TO_POINTER(1), test_notify); g_test_add_data_func(TEST_("notify/2"), GINT_TO_POINTER(0), test_notify); g_test_add_func(TEST_("notify/3"), test_notify2); test_init(&test_opt, argc, argv); test_config_init(&config, TMP_DIR_TEMPLATE); result = g_test_run(); test_config_cleanup(&config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_aidl2/000077500000000000000000000000001526127616700220045ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_aidl2/Makefile000066400000000000000000000001341526127616700234420ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_aidl2 include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_aidl2/unit_servicemanager_aidl2.c000066400000000000000000000347001526127616700272610ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_config.h" #include "gbinder_ipc.h" #include "gbinder_reader.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_remote_request.h" #include "gbinder_remote_object.h" #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager_aidl2-XXXXXX"; GType gbinder_servicemanager_hidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl3_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl5_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl5 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * Test service manager *==========================================================================*/ #define SVCMGR_HANDLE (0) static const char SVCMGR_IFACE[] = "android.os.IServiceManager"; enum servicemanager_aidl_tx { GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION }; const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL }; typedef GBinderLocalObjectClass ServiceManagerAidl2Class; typedef struct service_manager_aidl2 { GBinderLocalObject parent; GHashTable* objects; GMutex mutex; } ServiceManagerAidl2; #define SERVICE_MANAGER_AIDL2_TYPE (service_manager_aidl2_get_type()) #define SERVICE_MANAGER_AIDL2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ SERVICE_MANAGER_AIDL2_TYPE, ServiceManagerAidl2)) G_DEFINE_TYPE(ServiceManagerAidl2, service_manager_aidl2, \ GBINDER_TYPE_LOCAL_OBJECT) static GBinderLocalReply* servicemanager_aidl2_handler( ServiceManagerAidl2* self, GBinderRemoteRequest* req, guint code, int* status) { GBinderLocalObject* obj = &self->parent; GBinderLocalReply* reply = NULL; GBinderReader reader; GBinderRemoteObject* remote_obj; guint32 num, allow_isolated, dumpsys_priority; char* str; GDEBUG("%s %u", gbinder_remote_request_interface(req), code); *status = -1; /* Lock */ g_mutex_lock(&self->mutex); switch (code) { case GET_SERVICE_TRANSACTION: case CHECK_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); if (str) { reply = gbinder_local_object_new_reply(obj); remote_obj = g_hash_table_lookup(self->objects, str); if (remote_obj) { GDEBUG("Found name '%s' => %p", str, remote_obj); gbinder_local_reply_append_remote_object(reply, remote_obj); } else { GDEBUG("Name '%s' not found", str); gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK); } g_free(str); } break; case ADD_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); remote_obj = gbinder_reader_read_object(&reader); if (str && remote_obj && gbinder_reader_read_uint32(&reader, &allow_isolated) && gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { GDEBUG("Adding '%s'", str); g_hash_table_replace(self->objects, str, remote_obj); remote_obj = NULL; str = NULL; reply = gbinder_local_object_new_reply(obj); *status = GBINDER_STATUS_OK; } g_free(str); gbinder_remote_object_unref(remote_obj); break; case LIST_SERVICES_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); if (gbinder_reader_read_uint32(&reader, &num) && gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { if (num < g_hash_table_size(self->objects)) { GList* keys = g_hash_table_get_keys(self->objects); GList* l = g_list_nth(keys, num); /* Ignore dumpsys_priority */ reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_append_string16(reply, l->data); g_list_free(keys); *status = GBINDER_STATUS_OK; } else { GDEBUG("Index %u out of bounds", num); } } break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); /* Unlock */ return reply; } static GBinderLocalReply* servicemanager_aidl2_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return !g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE) ? servicemanager_aidl2_handler(SERVICE_MANAGER_AIDL2(obj), req, code, status) : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)-> handle_looper_transaction(obj, req, code, flags, status); } static GBINDER_LOCAL_TRANSACTION_SUPPORT servicemanager_aidl2_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Handle servicemanager transactions on the looper thread */ return !g_strcmp0(iface, SVCMGR_IFACE) ? GBINDER_LOCAL_TRANSACTION_LOOPER : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)-> can_handle_transaction(self, iface, code); } static void service_manager_aidl2_finalize( GObject* object) { ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); G_OBJECT_CLASS(service_manager_aidl2_parent_class)->finalize(object); } static void service_manager_aidl2_init( ServiceManagerAidl2* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); } static void service_manager_aidl2_class_init( ServiceManagerAidl2Class* klass) { G_OBJECT_CLASS(klass)->finalize = service_manager_aidl2_finalize; klass->can_handle_transaction = servicemanager_aidl2_can_handle_transaction; klass->handle_looper_transaction = servicemanager_aidl2_handle_looper_transaction; } static ServiceManagerAidl2* servicemanager_aidl2_new( const char* dev) { ServiceManagerAidl2* self = g_object_new(SERVICE_MANAGER_AIDL2_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces, NULL, NULL); test_binder_register_object(fd, obj, SVCMGR_HANDLE); gbinder_ipc_register_local_object(ipc, obj); gbinder_ipc_unref(ipc); return self; } /*==========================================================================* * Test context *==========================================================================*/ typedef struct test_context { TestConfig config; char* config_file; GBinderLocalObject* object; ServiceManagerAidl2* service; GBinderServiceManager* client; GMainLoop* loop; int fd; } TestContext; static void test_context_init( TestContext* test) { const char* dev = GBINDER_DEFAULT_BINDER; const char* config = "[Protocol]\n" "Default = aidl2\n" "/dev/binder = aidl2\n" "[ServiceManager]\n" "Default = aidl2\n" "/dev/binder = aidl2\n"; GBinderIpc* ipc; memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); GDEBUG("Config file %s", test->config_file); gbinder_config_file = test->config_file; ipc = gbinder_ipc_new(dev, NULL); test->fd = gbinder_driver_fd(ipc->driver); test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL); test_binder_register_object(test->fd, test->object, AUTO_HANDLE); test->service = servicemanager_aidl2_new(dev); test->client = gbinder_servicemanager_new(dev); test->loop = g_main_loop_new(NULL, FALSE); gbinder_ipc_unref(ipc); } static void test_context_deinit( TestContext* test) { test_binder_br_dead_binder_obj(test->fd, test->object); gbinder_local_object_unref(test->object); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(test->service)); gbinder_servicemanager_unref(test->client); test_binder_exit_wait(&test_opt, test->loop); remove(test->config_file); g_free(test->config_file); g_main_loop_unref(test->loop); test_config_cleanup(&test->config); } static void test_context_wait_ref_cb( GBinderLocalObject* obj, void* user_data) { GDEBUG("strong_refs %d", obj->strong_refs); if (obj->strong_refs > 0) { test_quit_later((GMainLoop*)user_data); } } static void test_context_wait_ref( TestContext* test) { /* Wait until the object gets referenced by servicemanager */ gulong id = gbinder_local_object_add_strong_refs_changed_handler (test->object, test_context_wait_ref_cb, test->loop); test_run(&test_opt, test->loop); gbinder_local_object_remove_handler(test->object, id); } /*==========================================================================* * get *==========================================================================*/ static void test_get_run() { TestContext test; const char* name = "name"; int status = -1; test_context_init(&test); /* Query the object (it's not there yet) */ GDEBUG("Querying '%s'", name); g_assert(!gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1); g_assert(g_hash_table_contains(test.service->objects, name)); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Query the object (this time it must be there) */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); GDEBUG("Done"); test_context_deinit(&test); } static void test_get() { test_run_in_context(&test_opt, test_get_run); } /*==========================================================================* * list *==========================================================================*/ static void test_list_run() { TestContext test; const char* name = "name"; char** list; test_context_init(&test); /* Request the list */ list = gbinder_servicemanager_list_sync(test.client); /* There's nothing there yet */ g_assert(list); g_assert(!list[0]); g_strfreev(list); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Request the list again */ list = gbinder_servicemanager_list_sync(test.client); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(list), == ,1); g_assert_cmpstr(list[0], == ,name); g_strfreev(list); GDEBUG("Done"); test_context_deinit(&test); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_aidl2/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("list"), test_list); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_aidl3/000077500000000000000000000000001526127616700220055ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_aidl3/Makefile000066400000000000000000000001341526127616700234430ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_aidl3 include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_aidl3/unit_servicemanager_aidl3.c000066400000000000000000000351451526127616700272670ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_config.h" #include "gbinder_ipc.h" #include "gbinder_reader.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_remote_request.h" #include "gbinder_remote_object.h" #include "gbinder_writer.h" #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager_aidl3-XXXXXX"; GType gbinder_servicemanager_hidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl5_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl5 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * Test service manager *==========================================================================*/ #define SVCMGR_HANDLE (0) static const char SVCMGR_IFACE[] = "android.os.IServiceManager"; enum servicemanager_aidl_tx { GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION }; const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL }; typedef GBinderLocalObjectClass ServiceManagerAidl3Class; typedef struct service_manager_aidl3 { GBinderLocalObject parent; GHashTable* objects; GMutex mutex; } ServiceManagerAidl3; #define SERVICE_MANAGER_AIDL3_TYPE (service_manager_aidl3_get_type()) #define SERVICE_MANAGER_AIDL3(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ SERVICE_MANAGER_AIDL3_TYPE, ServiceManagerAidl3)) G_DEFINE_TYPE(ServiceManagerAidl3, service_manager_aidl3, \ GBINDER_TYPE_LOCAL_OBJECT) static GBinderLocalReply* servicemanager_aidl3_handler( ServiceManagerAidl3* self, GBinderRemoteRequest* req, guint code, int* status) { GBinderLocalObject* obj = &self->parent; GBinderLocalReply* reply = NULL; GBinderReader reader; GBinderRemoteObject* remote_obj; guint32 allow_isolated, dumpsys_priority; char* str; GDEBUG("%s %u", gbinder_remote_request_interface(req), code); *status = -1; /* Lock */ g_mutex_lock(&self->mutex); switch (code) { case GET_SERVICE_TRANSACTION: case CHECK_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); if (str) { GBinderWriter writer; remote_obj = g_hash_table_lookup(self->objects, str); reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_remote_object(&writer, remote_obj); if (remote_obj) { GDEBUG("Found name '%s' => %p", str, remote_obj); } else { GDEBUG("Name '%s' not found", str); } g_free(str); } break; case ADD_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); remote_obj = gbinder_reader_read_object(&reader); if (str && remote_obj && gbinder_reader_read_uint32(&reader, &allow_isolated) && gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { GDEBUG("Adding '%s'", str); g_hash_table_replace(self->objects, str, remote_obj); remote_obj = NULL; str = NULL; reply = gbinder_local_object_new_reply(obj); *status = GBINDER_STATUS_OK; } g_free(str); gbinder_remote_object_unref(remote_obj); break; case LIST_SERVICES_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); if (gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { if (g_hash_table_size(self->objects) == 1) { GList* keys = g_hash_table_get_keys(self->objects); GList* l = g_list_nth(keys, 0); gint32 srv_size = 1; GBinderWriter writer; reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_int32(&writer, srv_size); gbinder_writer_append_string16(&writer, l->data); g_list_free(keys); *status = GBINDER_STATUS_OK; } else { GDEBUG("Incorrect number of services %u", g_hash_table_size(self->objects)); } } break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); /* Unlock */ return reply; } static GBinderLocalReply* servicemanager_aidl3_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return !g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE) ? servicemanager_aidl3_handler(SERVICE_MANAGER_AIDL3(obj), req, code, status) : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl3_parent_class)-> handle_looper_transaction(obj, req, code, flags, status); } static GBINDER_LOCAL_TRANSACTION_SUPPORT servicemanager_aidl3_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Handle servicemanager transactions on the looper thread */ return !g_strcmp0(iface, SVCMGR_IFACE) ? GBINDER_LOCAL_TRANSACTION_LOOPER : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl3_parent_class)-> can_handle_transaction(self, iface, code); } static void service_manager_aidl3_finalize( GObject* object) { ServiceManagerAidl3* self = SERVICE_MANAGER_AIDL3(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); G_OBJECT_CLASS(service_manager_aidl3_parent_class)->finalize(object); } static void service_manager_aidl3_init( ServiceManagerAidl3* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); } static void service_manager_aidl3_class_init( ServiceManagerAidl3Class* klass) { G_OBJECT_CLASS(klass)->finalize = service_manager_aidl3_finalize; klass->can_handle_transaction = servicemanager_aidl3_can_handle_transaction; klass->handle_looper_transaction = servicemanager_aidl3_handle_looper_transaction; } static ServiceManagerAidl3* servicemanager_aidl3_new( const char* dev) { ServiceManagerAidl3* self = g_object_new(SERVICE_MANAGER_AIDL3_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces, NULL, NULL); test_binder_register_object(fd, obj, SVCMGR_HANDLE); gbinder_ipc_register_local_object(ipc, obj); gbinder_ipc_unref(ipc); return self; } /*==========================================================================* * Test context *==========================================================================*/ typedef struct test_context { TestConfig config; char* config_file; GBinderLocalObject* object; ServiceManagerAidl3* service; GBinderServiceManager* client; GMainLoop* loop; int fd; } TestContext; static void test_context_init( TestContext* test) { const char* dev = GBINDER_DEFAULT_BINDER; const char* config = "[Protocol]\n" "Default = aidl3\n" "/dev/binder = aidl3\n" "[ServiceManager]\n" "Default = aidl3\n" "/dev/binder = aidl3\n"; GBinderIpc* ipc; memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); GDEBUG("Config file %s", test->config_file); gbinder_config_file = test->config_file; ipc = gbinder_ipc_new(dev, NULL); test->fd = gbinder_driver_fd(ipc->driver); test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL); test_binder_register_object(test->fd, test->object, AUTO_HANDLE); test->service = servicemanager_aidl3_new(dev); test->client = gbinder_servicemanager_new(dev); test->loop = g_main_loop_new(NULL, FALSE); gbinder_ipc_unref(ipc); } static void test_context_deinit( TestContext* test) { test_binder_br_dead_binder_obj(test->fd, test->object); gbinder_local_object_unref(test->object); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(test->service)); gbinder_servicemanager_unref(test->client); test_binder_exit_wait(&test_opt, test->loop); remove(test->config_file); g_free(test->config_file); g_main_loop_unref(test->loop); test_config_cleanup(&test->config); } static void test_context_wait_ref_cb( GBinderLocalObject* obj, void* user_data) { GDEBUG("strong_refs %d", obj->strong_refs); if (obj->strong_refs > 0) { test_quit_later((GMainLoop*)user_data); } } static void test_context_wait_ref( TestContext* test) { /* Wait until the object gets referenced by servicemanager */ gulong id = gbinder_local_object_add_strong_refs_changed_handler (test->object, test_context_wait_ref_cb, test->loop); test_run(&test_opt, test->loop); gbinder_local_object_remove_handler(test->object, id); } /*==========================================================================* * get *==========================================================================*/ static void test_get_run() { TestContext test; const char* name = "name"; int status = -1; test_context_init(&test); /* Query the object (it's not there yet) */ GDEBUG("Querying '%s'", name); g_assert(!gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1); g_assert(g_hash_table_contains(test.service->objects, name)); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Query the object (this time it must be there) */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); GDEBUG("Done"); test_context_deinit(&test); } static void test_get() { test_run_in_context(&test_opt, test_get_run); } /*==========================================================================* * list *==========================================================================*/ static void test_list_run() { TestContext test; const char* name = "name"; char** list; test_context_init(&test); /* Request the list */ list = gbinder_servicemanager_list_sync(test.client); /* There's nothing there yet */ g_assert(list); g_assert(!list[0]); g_strfreev(list); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Request the list again */ list = gbinder_servicemanager_list_sync(test.client); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(list), == ,1); g_assert_cmpstr(list[0], == ,name); g_strfreev(list); GDEBUG("Done"); test_context_deinit(&test); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_aidl3/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("list"), test_list); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_aidl5/000077500000000000000000000000001526127616700220075ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_aidl5/Makefile000066400000000000000000000001341526127616700234450ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_aidl5 include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_aidl5/unit_servicemanager_aidl5.c000066400000000000000000000373021526127616700272700ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_config.h" #include "gbinder_ipc.h" #include "gbinder_reader.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_remote_request.h" #include "gbinder_remote_object.h" #include "gbinder_writer.h" #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager_aidl5-XXXXXX"; GType gbinder_servicemanager_hidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * Test service manager *==========================================================================*/ #define SVCMGR_HANDLE (0) static const char SVCMGR_IFACE[] = "android.os.IServiceManager"; enum servicemanager_aidl_tx { AIDL5_GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, AIDL5_GET_SERVICE2_TRANSACTION, AIDL5_CHECK_SERVICE_TRANSACTION, AIDL5_ADD_SERVICE_TRANSACTION, AIDL5_LIST_SERVICES_TRANSACTION }; const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL }; typedef GBinderLocalObjectClass ServiceManagerAidl5Class; typedef struct service_manager_aidl5 { GBinderLocalObject parent; GHashTable* objects; GMutex mutex; gboolean return_service; } ServiceManagerAidl5; #define SERVICE_MANAGER_AIDL5_TYPE (service_manager_aidl5_get_type()) #define SERVICE_MANAGER_AIDL5(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ SERVICE_MANAGER_AIDL5_TYPE, ServiceManagerAidl5)) G_DEFINE_TYPE(ServiceManagerAidl5, service_manager_aidl5, \ GBINDER_TYPE_LOCAL_OBJECT) static GBinderLocalReply* servicemanager_aidl5_handler( ServiceManagerAidl5* self, GBinderRemoteRequest* req, guint code, int* status) { GBinderLocalObject* obj = &self->parent; GBinderLocalReply* reply = NULL; GBinderReader reader; GBinderRemoteObject* remote_obj; guint32 allow_isolated, dumpsys_priority; char* str; GDEBUG("%s %u", gbinder_remote_request_interface(req), code); *status = -1; /* Lock */ g_mutex_lock(&self->mutex); switch (code) { case AIDL5_GET_SERVICE_TRANSACTION: case AIDL5_CHECK_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); if (str) { GBinderWriter writer; remote_obj = g_hash_table_lookup(self->objects, str); reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); if (self->return_service) { GBinderWriter parcel; /* union Service { * ServiceWithMetadata serviceWithMetadata; * @nullable IBinder accessor; * } * * parcelable ServiceWithMetadata { * @nullable IBinder service; * boolean isLazyService; * } */ gbinder_writer_append_int32(&writer, 0); gbinder_writer_append_int32(&writer, 0); /* Tag */ gbinder_writer_start_parcelable(&writer, &parcel); gbinder_writer_append_remote_object(&parcel, remote_obj); gbinder_writer_append_bool(&parcel, FALSE); gbinder_writer_finish_parcelable(&parcel); } else { /* Just IBinder */ gbinder_writer_append_remote_object(&writer, remote_obj); } if (remote_obj) { GDEBUG("Found name '%s' => %p", str, remote_obj); } else { GDEBUG("Name '%s' not found", str); } g_free(str); } break; case AIDL5_ADD_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); remote_obj = gbinder_reader_read_object(&reader); if (str && remote_obj && gbinder_reader_read_uint32(&reader, &allow_isolated) && gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { GDEBUG("Adding '%s'", str); g_hash_table_replace(self->objects, str, remote_obj); remote_obj = NULL; str = NULL; reply = gbinder_local_object_new_reply(obj); *status = GBINDER_STATUS_OK; } g_free(str); gbinder_remote_object_unref(remote_obj); break; case AIDL5_LIST_SERVICES_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); if (gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { if (g_hash_table_size(self->objects) == 1) { GList* keys = g_hash_table_get_keys(self->objects); GList* l = g_list_nth(keys, 0); gint32 srv_size = 1; GBinderWriter writer; reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_int32(&writer, srv_size); gbinder_writer_append_string16(&writer, l->data); g_list_free(keys); *status = GBINDER_STATUS_OK; } else { GDEBUG("Incorrect number of services %u", g_hash_table_size(self->objects)); } } break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); /* Unlock */ return reply; } static GBinderLocalReply* servicemanager_aidl5_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return !g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE) ? servicemanager_aidl5_handler(SERVICE_MANAGER_AIDL5(obj), req, code, status) : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl5_parent_class)-> handle_looper_transaction(obj, req, code, flags, status); } static GBINDER_LOCAL_TRANSACTION_SUPPORT servicemanager_aidl5_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Handle servicemanager transactions on the looper thread */ return !g_strcmp0(iface, SVCMGR_IFACE) ? GBINDER_LOCAL_TRANSACTION_LOOPER : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl5_parent_class)-> can_handle_transaction(self, iface, code); } static void service_manager_aidl5_finalize( GObject* object) { ServiceManagerAidl5* self = SERVICE_MANAGER_AIDL5(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); G_OBJECT_CLASS(service_manager_aidl5_parent_class)->finalize(object); } static void service_manager_aidl5_init( ServiceManagerAidl5* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); } static void service_manager_aidl5_class_init( ServiceManagerAidl5Class* klass) { G_OBJECT_CLASS(klass)->finalize = service_manager_aidl5_finalize; klass->can_handle_transaction = servicemanager_aidl5_can_handle_transaction; klass->handle_looper_transaction = servicemanager_aidl5_handle_looper_transaction; } static ServiceManagerAidl5* servicemanager_aidl5_new( const char* dev, gboolean return_service) { ServiceManagerAidl5* self = g_object_new(SERVICE_MANAGER_AIDL5_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); self->return_service = return_service; gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces, NULL, NULL); test_binder_register_object(fd, obj, SVCMGR_HANDLE); gbinder_ipc_register_local_object(ipc, obj); gbinder_ipc_unref(ipc); return self; } /*==========================================================================* * Test context *==========================================================================*/ typedef struct test_context { TestConfig config; char* config_file; GBinderLocalObject* object; ServiceManagerAidl5* service; GBinderServiceManager* client; GMainLoop* loop; int fd; } TestContext; static void test_context_init( TestContext* test, gboolean return_service) { const char* dev = GBINDER_DEFAULT_BINDER; const char* config = "[Protocol]\n" "Default = aidl3\n" "/dev/binder = aidl3\n" "[ServiceManager]\n" "Default = aidl5\n" "/dev/binder = aidl5\n"; GBinderIpc* ipc; memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); GDEBUG("Config file %s", test->config_file); gbinder_config_file = test->config_file; ipc = gbinder_ipc_new(dev, NULL); test->fd = gbinder_driver_fd(ipc->driver); test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL); test_binder_register_object(test->fd, test->object, AUTO_HANDLE); test->service = servicemanager_aidl5_new(dev, return_service); test->client = gbinder_servicemanager_new(dev); test->loop = g_main_loop_new(NULL, FALSE); gbinder_ipc_unref(ipc); } static void test_context_deinit( TestContext* test) { test_binder_unregister_objects(test->fd); gbinder_local_object_unref(test->object); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(test->service)); gbinder_servicemanager_unref(test->client); test_binder_exit_wait(&test_opt, test->loop); remove(test->config_file); g_free(test->config_file); g_main_loop_unref(test->loop); test_config_cleanup(&test->config); } static void test_context_wait_ref_cb( GBinderLocalObject* obj, void* user_data) { GDEBUG("strong_refs %d", obj->strong_refs); if (obj->strong_refs > 0) { test_quit_later((GMainLoop*)user_data); } } static void test_context_wait_ref( TestContext* test) { /* Wait until the object gets referenced by servicemanager */ gulong id = gbinder_local_object_add_strong_refs_changed_handler (test->object, test_context_wait_ref_cb, test->loop); test_run(&test_opt, test->loop); gbinder_local_object_remove_handler(test->object, id); } /*==========================================================================* * get *==========================================================================*/ static void test_get_run( gconstpointer param) { TestContext test; const char* name = "name"; int status = -1; test_context_init(&test, GPOINTER_TO_INT(param)); /* Query the object (it's not there yet) */ GDEBUG("Querying '%s'", name); g_assert(!gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1); g_assert(g_hash_table_contains(test.service->objects, name)); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Query the object (this time it must be there) */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); test_context_deinit(&test); } static void test_get( gconstpointer param) { test_run_in_context_param(&test_opt, test_get_run, param); } /*==========================================================================* * list *==========================================================================*/ static void test_list_run() { TestContext test; const char* name = "name"; char** list; test_context_init(&test, FALSE); /* Request the list */ list = gbinder_servicemanager_list_sync(test.client); /* There's nothing there yet */ g_assert(list); g_assert(!list[0]); g_strfreev(list); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Request the list again */ list = gbinder_servicemanager_list_sync(test.client); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(list), == ,1); g_assert_cmpstr(list[0], == ,name); g_strfreev(list); test_context_deinit(&test); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_aidl5/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_data_func(TEST_("get/1"), GINT_TO_POINTER(0), test_get); g_test_add_data_func(TEST_("get/2"), GINT_TO_POINTER(1), test_get); g_test_add_func(TEST_("list"), test_list); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_aidl6/000077500000000000000000000000001526127616700220105ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_aidl6/Makefile000066400000000000000000000001341526127616700234460ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_aidl6 include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_aidl6/unit_servicemanager_aidl6.c000066400000000000000000000345331526127616700272750ustar00rootroot00000000000000/* * Copyright (C) 2020-2022 Jolla Ltd. * Copyright (C) 2023 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_config.h" #include "gbinder_ipc.h" #include "gbinder_reader.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_local_reply.h" #include "gbinder_remote_request.h" #include "gbinder_remote_object.h" #include "gbinder_writer.h" #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager_aidl6-XXXXXX"; GType gbinder_servicemanager_hidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_hidl */ g_assert_not_reached(); return 0; } /*==========================================================================* * Test service manager *==========================================================================*/ #define SVCMGR_HANDLE (0) static const char SVCMGR_IFACE[] = "android.os.IServiceManager"; enum servicemanager_aidl_tx { AIDL6_GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION, AIDL6_GET_SERVICE2_TRANSACTION, AIDL6_CHECK_SERVICE_TRANSACTION, AIDL6_CHECK_SERVICE2_TRANSACTION, AIDL6_ADD_SERVICE_TRANSACTION, AIDL6_LIST_SERVICES_TRANSACTION }; const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL }; typedef GBinderLocalObjectClass ServiceManagerAidl6Class; typedef struct service_manager_aidl6 { GBinderLocalObject parent; GHashTable* objects; GMutex mutex; } ServiceManagerAidl6; #define SERVICE_MANAGER_AIDL6_TYPE (service_manager_aidl6_get_type()) #define SERVICE_MANAGER_AIDL6(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ SERVICE_MANAGER_AIDL6_TYPE, ServiceManagerAidl6)) G_DEFINE_TYPE(ServiceManagerAidl6, service_manager_aidl6, \ GBINDER_TYPE_LOCAL_OBJECT) static GBinderLocalReply* servicemanager_aidl6_handler( ServiceManagerAidl6* self, GBinderRemoteRequest* req, guint code, int* status) { GBinderLocalObject* obj = &self->parent; GBinderLocalReply* reply = NULL; GBinderReader reader; GBinderRemoteObject* remote_obj; guint32 allow_isolated, dumpsys_priority; char* str; GDEBUG("%s %u", gbinder_remote_request_interface(req), code); *status = -1; /* Lock */ g_mutex_lock(&self->mutex); switch (code) { case AIDL6_GET_SERVICE_TRANSACTION: case AIDL6_CHECK_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); if (str) { GBinderWriter writer; remote_obj = g_hash_table_lookup(self->objects, str); reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_remote_object(&writer, remote_obj); if (remote_obj) { GDEBUG("Found name '%s' => %p", str, remote_obj); } else { GDEBUG("Name '%s' not found", str); } g_free(str); } break; case AIDL6_ADD_SERVICE_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); str = gbinder_reader_read_string16(&reader); remote_obj = gbinder_reader_read_object(&reader); if (str && remote_obj && gbinder_reader_read_uint32(&reader, &allow_isolated) && gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { GDEBUG("Adding '%s'", str); g_hash_table_replace(self->objects, str, remote_obj); remote_obj = NULL; str = NULL; reply = gbinder_local_object_new_reply(obj); *status = GBINDER_STATUS_OK; } g_free(str); gbinder_remote_object_unref(remote_obj); break; case AIDL6_LIST_SERVICES_TRANSACTION: gbinder_remote_request_init_reader(req, &reader); if (gbinder_reader_read_uint32(&reader, &dumpsys_priority)) { if (g_hash_table_size(self->objects) == 1) { GList* keys = g_hash_table_get_keys(self->objects); GList* l = g_list_nth(keys, 0); gint32 srv_size = 1; GBinderWriter writer; reply = gbinder_local_object_new_reply(obj); gbinder_local_reply_init_writer(reply, &writer); gbinder_writer_append_int32(&writer, GBINDER_STATUS_OK); gbinder_writer_append_int32(&writer, srv_size); gbinder_writer_append_string16(&writer, l->data); g_list_free(keys); *status = GBINDER_STATUS_OK; } else { GDEBUG("Incorrect number of services %u", g_hash_table_size(self->objects)); } } break; default: GDEBUG("Unhandled command %u", code); break; } g_mutex_unlock(&self->mutex); /* Unlock */ return reply; } static GBinderLocalReply* servicemanager_aidl6_handle_looper_transaction( GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code, guint flags, int* status) { return !g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE) ? servicemanager_aidl6_handler(SERVICE_MANAGER_AIDL6(obj), req, code, status) : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl6_parent_class)-> handle_looper_transaction(obj, req, code, flags, status); } static GBINDER_LOCAL_TRANSACTION_SUPPORT servicemanager_aidl6_can_handle_transaction( GBinderLocalObject* self, const char* iface, guint code) { /* Handle servicemanager transactions on the looper thread */ return !g_strcmp0(iface, SVCMGR_IFACE) ? GBINDER_LOCAL_TRANSACTION_LOOPER : GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl6_parent_class)-> can_handle_transaction(self, iface, code); } static void service_manager_aidl6_finalize( GObject* object) { ServiceManagerAidl6* self = SERVICE_MANAGER_AIDL6(object); g_mutex_clear(&self->mutex); g_hash_table_destroy(self->objects); G_OBJECT_CLASS(service_manager_aidl6_parent_class)->finalize(object); } static void service_manager_aidl6_init( ServiceManagerAidl6* self) { g_mutex_init(&self->mutex); self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gbinder_remote_object_unref); } static void service_manager_aidl6_class_init( ServiceManagerAidl6Class* klass) { G_OBJECT_CLASS(klass)->finalize = service_manager_aidl6_finalize; klass->can_handle_transaction = servicemanager_aidl6_can_handle_transaction; klass->handle_looper_transaction = servicemanager_aidl6_handle_looper_transaction; } static ServiceManagerAidl6* servicemanager_aidl6_new( const char* dev) { ServiceManagerAidl6* self = g_object_new(SERVICE_MANAGER_AIDL6_TYPE, NULL); GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self); GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces, NULL, NULL); test_binder_register_object(fd, obj, SVCMGR_HANDLE); gbinder_ipc_register_local_object(ipc, obj); gbinder_ipc_unref(ipc); return self; } /*==========================================================================* * Test context *==========================================================================*/ typedef struct test_context { TestConfig config; char* config_file; GBinderLocalObject* object; ServiceManagerAidl6* service; GBinderServiceManager* client; GMainLoop* loop; int fd; } TestContext; static void test_context_init( TestContext* test) { const char* dev = GBINDER_DEFAULT_BINDER; const char* config = "[Protocol]\n" "Default = aidl3\n" "/dev/binder = aidl3\n" "[ServiceManager]\n" "Default = aidl6\n" "/dev/binder = aidl6\n"; GBinderIpc* ipc; memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); GDEBUG("Config file %s", test->config_file); gbinder_config_file = test->config_file; ipc = gbinder_ipc_new(dev, NULL); test->fd = gbinder_driver_fd(ipc->driver); test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL); test_binder_register_object(test->fd, test->object, AUTO_HANDLE); test->service = servicemanager_aidl6_new(dev); test->client = gbinder_servicemanager_new(dev); test->loop = g_main_loop_new(NULL, FALSE); gbinder_ipc_unref(ipc); } static void test_context_deinit( TestContext* test) { test_binder_unregister_objects(test->fd); gbinder_local_object_unref(test->object); gbinder_local_object_unref(GBINDER_LOCAL_OBJECT(test->service)); gbinder_servicemanager_unref(test->client); test_binder_exit_wait(&test_opt, test->loop); remove(test->config_file); g_free(test->config_file); g_main_loop_unref(test->loop); test_config_cleanup(&test->config); } static void test_context_wait_ref_cb( GBinderLocalObject* obj, void* user_data) { GDEBUG("strong_refs %d", obj->strong_refs); if (obj->strong_refs > 0) { test_quit_later((GMainLoop*)user_data); } } static void test_context_wait_ref( TestContext* test) { /* Wait until the object gets referenced by servicemanager */ gulong id = gbinder_local_object_add_strong_refs_changed_handler (test->object, test_context_wait_ref_cb, test->loop); test_run(&test_opt, test->loop); gbinder_local_object_remove_handler(test->object, id); } /*==========================================================================* * get *==========================================================================*/ static void test_get_run() { TestContext test; const char* name = "name"; int status = -1; test_context_init(&test); /* Query the object (it's not there yet) */ GDEBUG("Querying '%s'", name); g_assert(!gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1); g_assert(g_hash_table_contains(test.service->objects, name)); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Query the object (this time it must be there) */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service_sync(test.client, name, &status)); g_assert_cmpint(status, == ,GBINDER_STATUS_OK); test_context_deinit(&test); } static void test_get() { test_run_in_context(&test_opt, test_get_run); } /*==========================================================================* * list *==========================================================================*/ static void test_list_run() { TestContext test; const char* name = "name"; char** list; test_context_init(&test); /* Request the list */ list = gbinder_servicemanager_list_sync(test.client); /* There's nothing there yet */ g_assert(list); g_assert(!list[0]); g_strfreev(list); /* Register object */ GDEBUG("Registering object '%s' => %p", name, test.object); g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client, name, test.object), == ,GBINDER_STATUS_OK); /* Wait until the object gets referenced by servicemanager */ test_context_wait_ref(&test); /* Request the list again */ list = gbinder_servicemanager_list_sync(test.client); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(list), == ,1); g_assert_cmpstr(list[0], == ,name); g_strfreev(list); test_context_deinit(&test); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_aidl6/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("list"), test_list); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicemanager_hidl/000077500000000000000000000000001526127616700217315ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicemanager_hidl/Makefile000066400000000000000000000002351526127616700233710ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicemanager_hidl COMMON_SRC = test_binder.c test_main.c test_servicemanager_hidl.c include ../common/Makefile libgbinder-1.1.54/unit/unit_servicemanager_hidl/unit_servicemanager_hidl.c000066400000000000000000000315071526127616700271350ustar00rootroot00000000000000/* * Copyright (C) 2021-2024 Slava Monich * Copyright (C) 2021-2022 Jolla Ltd. * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "test_servicemanager_hidl.h" #include "gbinder_ipc.h" #include "gbinder_config.h" #include "gbinder_driver.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include #include static TestOpt test_opt; #define DEV GBINDER_DEFAULT_HWBINDER static const char TMP_DIR_TEMPLATE[] = "gbinder-test-svcmgr-hidl-XXXXXX"; static const char DEFAULT_CONFIG_DATA[] = "[Protocol]\n" DEV " = hidl\n" "[ServiceManager]\n" DEV " = hidl\n"; GType gbinder_servicemanager_aidl_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl2_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl2 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl3_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl3 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl5_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl5 */ g_assert_not_reached(); return 0; } GType gbinder_servicemanager_aidl6_get_type() { /* Dummy function to avoid pulling in gbinder_servicemanager_aidl6 */ g_assert_not_reached(); return 0; } /*==========================================================================* * Common *==========================================================================*/ static TestServiceManagerHidl* test_servicemanager_impl_new( const char* dev) { GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); TestServiceManagerHidl* sm = test_servicemanager_hidl_new(ipc); test_binder_register_object(fd, GBINDER_LOCAL_OBJECT(sm), GBINDER_SERVICEMANAGER_HANDLE); gbinder_ipc_unref(ipc); return sm; } /*==========================================================================* * get *==========================================================================*/ static void test_add_cb( GBinderServiceManager* sm, int status, void* user_data) { GDEBUG("Name added"); g_assert(status == GBINDER_STATUS_OK); if (user_data) { g_main_loop_quit(user_data); } } static void test_get_none_cb( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(!obj); g_assert(status == GBINDER_STATUS_OK); g_main_loop_quit(user_data); } static void test_get_cb( GBinderServiceManager* sm, GBinderRemoteObject* obj, int status, void* user_data) { g_assert(obj); g_assert(status == GBINDER_STATUS_OK); g_main_loop_quit(user_data); } static void test_get_run() { GBinderIpc* ipc; TestServiceManagerHidl* smsvc; GBinderLocalObject* obj; int fd; GBinderServiceManager* sm; GMainLoop* loop = g_main_loop_new(NULL, FALSE); const char* name = "android.hidl.base@1.0::IBase/test"; ipc = gbinder_ipc_new(DEV, NULL); smsvc = test_servicemanager_impl_new(DEV); obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); fd = gbinder_driver_fd(ipc->driver); /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(DEV); /* This one fails because of unexpected name format */ g_assert(!gbinder_servicemanager_get_service_sync(sm, "test", NULL)); /* Query the object (it's not there yet) and wait for completion */ GDEBUG("Querying '%s'", name); g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb, loop)); test_run(&test_opt, loop); /* Register object and wait for completion */ GDEBUG("Registering object '%s' => %p", name, obj); g_assert(gbinder_servicemanager_add_service(sm, name, obj, test_add_cb, loop)); test_run(&test_opt, loop); g_assert_cmpuint(test_servicemanager_hidl_object_count(smsvc), == ,1); g_assert(test_servicemanager_hidl_lookup(smsvc, name)); /* Query the object (this time it must be there) and wait for completion */ GDEBUG("Querying '%s' again", name); g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop)); test_run(&test_opt, loop); gbinder_local_object_unref(obj); test_servicemanager_hidl_free(smsvc); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_get() { test_run_in_context(&test_opt, test_get_run); } /*==========================================================================* * list *==========================================================================*/ typedef struct test_list { char** list; GMainLoop* loop; } TestList; static gboolean test_list_cb( GBinderServiceManager* sm, char** services, void* user_data) { TestList* test = user_data; GDEBUG("Got %u name(s)", gutil_strv_length(services)); g_strfreev(test->list); test->list = services; g_main_loop_quit(test->loop); return TRUE; } static void test_list_run() { TestList test; GBinderIpc* ipc; TestServiceManagerHidl* smsvc; GBinderLocalObject* obj; int fd; GBinderServiceManager* sm; const char* name = "android.hidl.base@1.0::IBase/test"; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); ipc = gbinder_ipc_new(DEV, NULL); smsvc = test_servicemanager_impl_new(DEV); obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); fd = gbinder_driver_fd(ipc->driver); /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(DEV); /* Request the list and wait for completion */ g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test)); test_run(&test_opt, test.loop); /* There's nothing there yet */ g_assert(test.list); g_assert(!test.list[0]); /* Register object and wait for completion */ g_assert(gbinder_servicemanager_add_service(sm, name, obj, test_add_cb, test.loop)); test_run(&test_opt, test.loop); /* Request the list again */ g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test)); test_run(&test_opt, test.loop); /* Now the name must be there */ g_assert_cmpuint(gutil_strv_length(test.list), == ,1); g_assert_cmpstr(test.list[0], == ,name); gbinder_local_object_unref(obj); test_servicemanager_hidl_free(smsvc); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, test.loop); g_strfreev(test.list); g_main_loop_unref(test.loop); } static void test_list() { test_run_in_context(&test_opt, test_list_run); } /*==========================================================================* * notify *==========================================================================*/ typedef struct test_notify { GMainLoop* loop; TestServiceManagerHidl* smsvc; int notify_count; gboolean name_added; } TestNotify; static void test_notify_never( GBinderServiceManager* sm, const char* name, void* user_data) { g_assert_not_reached(); } static void test_notify_add_cb( GBinderServiceManager* sm, int status, void* user_data) { TestNotify* test = user_data; GDEBUG("Name added"); g_assert(status == GBINDER_STATUS_OK); g_assert(!test->name_added); test->name_added = TRUE; if (test->notify_count) { g_main_loop_quit(test->loop); } } static void test_notify_unreached_cb( GBinderServiceManager* sm, const char* name, void* user_data) { g_assert_not_reached(); } static void test_notify_cb( GBinderServiceManager* sm, const char* name, void* user_data) { TestNotify* test = user_data; g_assert(name); GDEBUG("'%s' is registered", name); g_assert_cmpint(test->notify_count, == ,0); test->notify_count++; /* Exit the loop after both things happen */ if (test->name_added) { g_main_loop_quit(test->loop); } } static void test_notify_run() { TestNotify test; GBinderIpc* ipc; GBinderLocalObject* obj; int fd; GBinderServiceManager* sm; const char* name = "android.hidl.base@1.0::IBase/test"; gulong id; memset(&test, 0, sizeof(test)); test.loop = g_main_loop_new(NULL, FALSE); ipc = gbinder_ipc_new(DEV, NULL); test.smsvc = test_servicemanager_impl_new(DEV); obj = gbinder_local_object_new(ipc, NULL, NULL, NULL); fd = gbinder_driver_fd(ipc->driver); /* Set up binder simulator */ test_binder_register_object(fd, obj, AUTO_HANDLE); sm = gbinder_servicemanager_new(DEV); /* This one fails because of invalid names */ g_assert(!gbinder_servicemanager_add_registration_handler(sm, NULL, test_notify_never, NULL)); g_assert(!gbinder_servicemanager_add_registration_handler(sm, "", test_notify_never, NULL)); g_assert(!gbinder_servicemanager_add_registration_handler(sm, ",", test_notify_never, NULL)); /* Register and immediately unregister the handler */ id = gbinder_servicemanager_add_registration_handler(sm, name, test_notify_unreached_cb, NULL); g_assert(id); gbinder_servicemanager_remove_handler(sm, id); /* Actually start watching */ id = gbinder_servicemanager_add_registration_handler(sm, name, test_notify_cb, &test); g_assert(id); /* Register the object and wait for completion */ GDEBUG("Registering object '%s' => %p", name, obj); g_assert(gbinder_servicemanager_add_service(sm, name, obj, test_notify_add_cb, &test)); /* The loop quits after the name is added and notification is received */ test_run(&test_opt, test.loop); gbinder_servicemanager_remove_handler(sm, id); gbinder_local_object_unref(obj); test_servicemanager_hidl_free(test.smsvc); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, test.loop); g_main_loop_unref(test.loop); } static void test_notify() { test_run_in_context(&test_opt, test_notify_run); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicemanager_hidl/" t int main(int argc, char* argv[]) { TestConfig test_config; char* config_file; int result; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("get"), test_get); g_test_add_func(TEST_("list"), test_list); g_test_add_func(TEST_("notify"), test_notify); test_init(&test_opt, argc, argv); test_config_init(&test_config, TMP_DIR_TEMPLATE); config_file = g_build_filename(test_config.config_dir, "test.conf", NULL); g_assert(g_file_set_contents(config_file, DEFAULT_CONFIG_DATA, -1, NULL)); GDEBUG("Config file %s", config_file); gbinder_config_file = config_file; result = g_test_run(); remove(config_file); g_free(config_file); test_config_cleanup(&test_config); return result; } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicename/000077500000000000000000000000001526127616700202375ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicename/Makefile000066400000000000000000000001231526127616700216730ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicename include ../common/Makefile libgbinder-1.1.54/unit/unit_servicename/unit_servicename.c000066400000000000000000000364351526127616700237560ustar00rootroot00000000000000/* * Copyright (C) 2019-2022 Jolla Ltd. * Copyright (C) 2019-2022 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_servicename.h" #include "gbinder_servicemanager_p.h" #include "gbinder_local_object_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include #include #include static TestOpt test_opt; static void test_quit( GBinderServiceManager* sm, void* user_data) { test_quit_later((GMainLoop*)user_data); } static void test_setup_ping( GBinderIpc* ipc) { const int fd = gbinder_driver_fd(ipc->driver); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); } /*==========================================================================* * TestServiceManager *==========================================================================*/ typedef GBinderServiceManagerClass TestServiceManagerClass; typedef struct test_servicemanager { GBinderServiceManager manager; GCond cond; GMutex mutex; char** services; gboolean block_add; int add_fail; int add_result; } TestServiceManager; G_DEFINE_TYPE(TestServiceManager, test_servicemanager, GBINDER_TYPE_SERVICEMANAGER) #define TEST_SERVICEMANAGER_IFACE "android.os.IServiceManager" #define TEST_TYPE_SERVICEMANAGER (test_servicemanager_get_type()) #define TEST_SERVICEMANAGER(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), \ TEST_TYPE_SERVICEMANAGER, TestServiceManager) static char** test_servicemanager_list( GBinderServiceManager* manager, const GBinderIpcSyncApi* api) { char** ret; TestServiceManager* self = TEST_SERVICEMANAGER(manager); g_mutex_lock(&self->mutex); ret = g_strdupv(self->services); GDEBUG("%u", gutil_strv_length(ret)); g_mutex_unlock(&self->mutex); return ret; } static GBinderRemoteObject* test_servicemanager_get_service( GBinderServiceManager* manager, const char* name, int* status, const GBinderIpcSyncApi* api) { *status = (-ENOENT); return NULL; } static int test_servicemanager_add_service( GBinderServiceManager* manager, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api) { TestServiceManager* self = TEST_SERVICEMANAGER(manager); int result; g_mutex_lock(&self->mutex); if (self->add_fail > 0) { self->add_fail--; result = -EFAULT; } else { result = self->add_result; if (result == GBINDER_STATUS_OK) { if (!gutil_strv_contains(self->services, name)) { self->services = gutil_strv_add(self->services, name); } if (self->block_add) { while (self->block_add) { g_cond_wait(&self->cond, &self->mutex); } } else { test_binder_br_dead_binder(gbinder_driver_fd(obj->ipc->driver), ANY_THREAD, GBINDER_SERVICEMANAGER_HANDLE); } } } g_mutex_unlock(&self->mutex); return result; } static GBINDER_SERVICEMANAGER_NAME_CHECK test_servicemanager_check_name( GBinderServiceManager* manager, const char* name) { return name ? GBINDER_SERVICEMANAGER_NAME_INVALID : GBINDER_SERVICEMANAGER_NAME_OK; } static gboolean test_servicemanager_watch( GBinderServiceManager* manager, const char* name) { return TRUE; } static void test_servicemanager_unwatch( GBinderServiceManager* manager, const char* name) { } static void test_servicemanager_init( TestServiceManager* self) { g_cond_init(&self->cond); g_mutex_init(&self->mutex); self->add_result = GBINDER_STATUS_OK; } static void test_servicemanager_finalize( GObject* object) { TestServiceManager* self = TEST_SERVICEMANAGER(object); g_cond_clear(&self->cond); g_mutex_clear(&self->mutex); g_strfreev(self->services); G_OBJECT_CLASS(test_servicemanager_parent_class)->finalize(object); } static void test_servicemanager_class_init( TestServiceManagerClass* klass) { klass->iface = TEST_SERVICEMANAGER_IFACE; klass->default_device = GBINDER_DEFAULT_HWBINDER; klass->list = test_servicemanager_list; klass->get_service = test_servicemanager_get_service; klass->add_service = test_servicemanager_add_service; klass->check_name = test_servicemanager_check_name; klass->watch = test_servicemanager_watch; klass->unwatch = test_servicemanager_unwatch; G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize; } /* Avoid pulling in the actual objects */ GType gbinder_servicemanager_aidl_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl2_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl3_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl5_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl6_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_hidl_get_type() { return TEST_TYPE_SERVICEMANAGER; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderServiceManager* sm; test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); g_assert(!gbinder_servicename_new(NULL, NULL, NULL)); g_assert(!gbinder_servicename_new(sm, NULL, NULL)); g_assert(!gbinder_servicename_ref(NULL)); gbinder_servicename_unref(NULL); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { const char* obj_name = "test"; const char* dev = GBINDER_DEFAULT_BINDER; const char* const ifaces[] = { "interface", NULL }; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj; GBinderServiceManager* sm; GBinderServiceName* sn; test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); g_assert(!gbinder_servicename_new(sm, obj, NULL)); sn = gbinder_servicename_new(sm, obj, obj_name); g_assert(sn); g_assert_cmpstr(sn->name, == ,obj_name); g_assert(gbinder_servicename_ref(sn) == sn); gbinder_servicename_unref(sn); gbinder_servicename_unref(sn); gbinder_local_object_unref(obj); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * present *==========================================================================*/ static void test_present( int add_result) { const char* obj_name = "test"; const char* const ifaces[] = { "interface", NULL }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj; GBinderServiceManager* sm; GBinderServiceName* sn; gulong id; test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); TEST_SERVICEMANAGER(sm)->add_result = add_result; obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); sn = gbinder_servicename_new(sm, obj, obj_name); g_assert(sn); g_assert_cmpstr(sn->name, == ,obj_name); /* Immediately generate death notification (need looper for that) */ test_binder_br_dead_binder(fd, ANY_THREAD, GBINDER_SERVICEMANAGER_HANDLE); id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); test_run(&test_opt, loop); gbinder_servicename_unref(sn); gbinder_local_object_unref(obj); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } static void test_present_ok( void) { test_present(GBINDER_STATUS_OK); } static void test_present_err( void) { test_present(-1); } /*==========================================================================* * not_present *==========================================================================*/ static void test_not_present( void) { const char* obj_name = "test"; const char* const ifaces[] = { "interface", NULL }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj; GBinderServiceManager* sm; GBinderServiceName* sn; gulong id; /* This makes presence detection PING fail */ test_binder_br_reply_status(fd, THIS_THREAD, -1); sm = gbinder_servicemanager_new(dev); g_assert(!gbinder_servicemanager_is_present(sm)); id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); sn = gbinder_servicename_new(sm, obj, obj_name); g_assert(sn); g_assert_cmpstr(sn->name, == ,obj_name); /* Make the next presence detection PING succeed */ test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); test_run(&test_opt, loop); gbinder_servicename_unref(sn); gbinder_local_object_unref(obj); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * retry *==========================================================================*/ static void test_retry( void) { const char* obj_name = "test"; const char* const ifaces[] = { "interface", NULL }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj; GBinderServiceManager* sm; GBinderServiceName* sn; gulong id; test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); TEST_SERVICEMANAGER(sm)->add_fail = 1; obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); sn = gbinder_servicename_new(sm, obj, obj_name); g_assert(sn); g_assert_cmpstr(sn->name, == ,obj_name); id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); test_run(&test_opt, loop); gbinder_servicename_unref(sn); gbinder_local_object_unref(obj); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * cancel *==========================================================================*/ static void test_cancel( void) { const char* obj_name = "test"; const char* const ifaces[] = { "interface", NULL }; const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); const int fd = gbinder_driver_fd(ipc->driver); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderLocalObject* obj; GBinderServiceManager* sm; TestServiceManager* test; GBinderServiceName* sn; gulong id; test_setup_ping(ipc); sm = gbinder_servicemanager_new(dev); obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL); /* Block name add calls */ test = TEST_SERVICEMANAGER(sm); g_mutex_lock(&test->mutex); test->block_add = TRUE; g_mutex_unlock(&test->mutex); /* This adds the name but the call blocks */ sn = gbinder_servicename_new(sm, obj, obj_name); g_assert(sn); g_assert_cmpstr(sn->name, == ,obj_name); /* Immediately generate death notification */ test_binder_br_dead_binder(fd, ANY_THREAD, GBINDER_SERVICEMANAGER_HANDLE); id = gbinder_servicemanager_add_presence_handler(sm, test_quit, loop); test_run(&test_opt, loop); /* Add call is supposed to be cancelled */ gbinder_servicename_unref(sn); gbinder_local_object_unref(obj); gbinder_servicemanager_remove_handler(sm, id); gbinder_servicemanager_unref(sm); /* Unblock pending add */ g_mutex_lock(&test->mutex); test->block_add = FALSE; g_cond_signal(&test->cond); g_mutex_unlock(&test->mutex); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(test) "/servicename/" test int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("present_ok"), test_present_ok); g_test_add_func(TEST_("present_err"), test_present_err); g_test_add_func(TEST_("not_present"), test_not_present); g_test_add_func(TEST_("retry"), test_retry); g_test_add_func(TEST_("cancel"), test_cancel); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_servicepoll/000077500000000000000000000000001526127616700202655ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_servicepoll/Makefile000066400000000000000000000001231526127616700217210ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_servicepoll include ../common/Makefile libgbinder-1.1.54/unit/unit_servicepoll/unit_servicepoll.c000066400000000000000000000337561526127616700240350ustar00rootroot00000000000000/* * Copyright (C) 2018-2022 Jolla Ltd. * Copyright (C) 2018-2022 Slava Monich * Copyright (C) 2026 Jolla Mobile Ltd * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_binder.h" #include "gbinder_driver.h" #include "gbinder_ipc.h" #include "gbinder_servicemanager_p.h" #include "gbinder_servicepoll.h" #include "gbinder_rpc_protocol.h" #include #include #include static TestOpt test_opt; static void test_setup_ping( GBinderIpc* ipc) { const int fd = gbinder_driver_fd(ipc->driver); test_binder_br_noop(fd, THIS_THREAD); test_binder_br_transaction_complete(fd, THIS_THREAD); test_binder_br_reply(fd, THIS_THREAD, 0, 0, NULL); } /*==========================================================================* * TestServiceManager *==========================================================================*/ typedef GBinderServiceManagerClass TestServiceManagerClass; typedef struct test_servicemanager { GBinderServiceManager manager; GMutex mutex; char** services; } TestServiceManager; G_DEFINE_TYPE(TestServiceManager, test_servicemanager, GBINDER_TYPE_SERVICEMANAGER) #define TEST_SERVICEMANAGER_IFACE "android.os.IServiceManager" #define TEST_TYPE_SERVICEMANAGER (test_servicemanager_get_type()) #define TEST_SERVICEMANAGER(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), \ TEST_TYPE_SERVICEMANAGER, TestServiceManager) static char** test_servicemanager_list( GBinderServiceManager* manager, const GBinderIpcSyncApi* api) { char** ret; TestServiceManager* self = TEST_SERVICEMANAGER(manager); g_mutex_lock(&self->mutex); ret = g_strdupv(self->services); GDEBUG("%u", gutil_strv_length(ret)); g_mutex_unlock(&self->mutex); return ret; } static GBinderRemoteObject* test_servicemanager_get_service( GBinderServiceManager* manager, const char* name, int* status, const GBinderIpcSyncApi* api) { *status = (-ENOENT); return NULL; } static int test_servicemanager_add_service( GBinderServiceManager* manager, const char* name, GBinderLocalObject* obj, const GBinderIpcSyncApi* api) { TestServiceManager* self = TEST_SERVICEMANAGER(manager); g_mutex_lock(&self->mutex); if (!gutil_strv_contains(self->services, name)) { self->services = gutil_strv_add(self->services, name); } g_mutex_unlock(&self->mutex); return GBINDER_STATUS_OK; } static GBINDER_SERVICEMANAGER_NAME_CHECK test_servicemanager_check_name( GBinderServiceManager* manager, const char* name) { return name ? GBINDER_SERVICEMANAGER_NAME_INVALID : GBINDER_SERVICEMANAGER_NAME_OK; } static gboolean test_servicemanager_watch( GBinderServiceManager* manager, const char* name) { return TRUE; } static void test_servicemanager_unwatch( GBinderServiceManager* manager, const char* name) { } static void test_servicemanager_init( TestServiceManager* self) { g_mutex_init(&self->mutex); } static void test_servicemanager_finalize( GObject* object) { TestServiceManager* self = TEST_SERVICEMANAGER(object); g_mutex_clear(&self->mutex); g_strfreev(self->services); G_OBJECT_CLASS(test_servicemanager_parent_class)->finalize(object); } static void test_servicemanager_class_init( TestServiceManagerClass* klass) { klass->iface = TEST_SERVICEMANAGER_IFACE; klass->default_device = GBINDER_DEFAULT_HWBINDER; klass->list = test_servicemanager_list; klass->get_service = test_servicemanager_get_service; klass->add_service = test_servicemanager_add_service; klass->check_name = test_servicemanager_check_name; klass->watch = test_servicemanager_watch; klass->unwatch = test_servicemanager_unwatch; G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize; } /* Avoid pulling in the actual objects */ GType gbinder_servicemanager_aidl_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl2_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl3_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl5_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_aidl6_get_type() { return TEST_TYPE_SERVICEMANAGER; } GType gbinder_servicemanager_hidl_get_type() { return TEST_TYPE_SERVICEMANAGER; } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { g_assert(!gbinder_servicepoll_ref(NULL)); g_assert(!gbinder_servicepoll_manager(NULL)); g_assert(!gbinder_servicepoll_is_known_name(NULL, "")); g_assert(!gbinder_servicepoll_add_handler(NULL, NULL, NULL)); gbinder_servicepoll_remove_handler(NULL, 0); gbinder_servicepoll_unref(NULL); } /*==========================================================================* * basic *==========================================================================*/ static void test_basic( void) { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GBinderServicePoll* weakptr = NULL; GBinderServiceManager* manager; GBinderServicePoll* poll; test_setup_ping(ipc); manager = gbinder_servicemanager_new(dev); poll = gbinder_servicepoll_new(manager, NULL); g_assert(poll); g_assert(gbinder_servicepoll_manager(poll) == manager); g_assert(!gbinder_servicepoll_is_known_name(poll, "foo")); g_assert(!gbinder_servicepoll_add_handler(poll, NULL, NULL)); gbinder_servicepoll_remove_handler(poll, 0); /* this does nothing */ gbinder_servicepoll_unref(poll); poll = gbinder_servicepoll_new(manager, &weakptr); g_assert(poll == weakptr); g_assert(poll == gbinder_servicepoll_new(manager, &weakptr)); gbinder_servicepoll_unref(poll); gbinder_servicepoll_unref(poll); gbinder_servicemanager_unref(manager); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * notify1 *==========================================================================*/ static void test_notify_proc( GBinderServicePoll* poll, const char* name_added, void* user_data) { GDEBUG("\"%s\" added", name_added); if (!g_strcmp0(name_added, "foo")) { test_quit_later((GMainLoop*)user_data); } } static gboolean test_notify1_foo( gpointer user_data) { TestServiceManager* test = user_data; g_mutex_lock(&test->mutex); GDEBUG("adding \"foo\""); test->services = gutil_strv_add(test->services, "foo"); g_mutex_unlock(&test->mutex); return G_SOURCE_REMOVE; } static gboolean test_notify1_bar( gpointer user_data) { TestServiceManager* test = user_data; g_mutex_lock(&test->mutex); GDEBUG("adding \"bar\""); test->services = gutil_strv_add(test->services, "bar"); g_mutex_unlock(&test->mutex); return G_SOURCE_REMOVE; } static void test_notify1( void) { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderServicePoll* weakptr = NULL; GBinderServiceManager* manager; TestServiceManager* test; GBinderServicePoll* poll; gulong id; test_setup_ping(ipc); manager = gbinder_servicemanager_new(dev); test = TEST_SERVICEMANAGER(manager); gbinder_servicepoll_interval_ms = 100; poll = gbinder_servicepoll_new(manager, &weakptr); g_timeout_add(2 * gbinder_servicepoll_interval_ms, test_notify1_bar, test); g_timeout_add(4 * gbinder_servicepoll_interval_ms, test_notify1_foo, test); id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop); g_assert(id); test_run(&test_opt, loop); g_assert(gbinder_servicepoll_is_known_name(poll, "foo")); g_assert(gbinder_servicepoll_is_known_name(poll, "bar")); gbinder_servicepoll_remove_handler(poll, id); gbinder_servicepoll_unref(poll); g_assert(!weakptr); gbinder_servicemanager_unref(manager); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * notify2 *==========================================================================*/ static gboolean test_notify2_foo( gpointer user_data) { TestServiceManager* test = user_data; g_mutex_lock(&test->mutex); GDEBUG("services = [\"bar\",\"foo\"]"); g_strfreev(test->services); test->services = g_strsplit("bar,bar3,foo", ",", -1); g_mutex_unlock(&test->mutex); return G_SOURCE_REMOVE; } static gboolean test_notify2_bar( gpointer user_data) { TestServiceManager* test = user_data; g_mutex_lock(&test->mutex); GDEBUG("services = [\"bar1\",\"bar2\",\"bar3\"]"); g_strfreev(test->services); test->services = g_strsplit("bar1,bar2,bar3", ",", -1); g_mutex_unlock(&test->mutex); return G_SOURCE_REMOVE; } static void test_notify2( void) { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderServicePoll* weakptr = NULL; GBinderServiceManager* manager; TestServiceManager* test; GBinderServicePoll* poll; gulong id; test_setup_ping(ipc); manager = gbinder_servicemanager_new(dev); test = TEST_SERVICEMANAGER(manager); gbinder_servicepoll_interval_ms = 100; poll = gbinder_servicepoll_new(manager, &weakptr); g_timeout_add(2 * gbinder_servicepoll_interval_ms, test_notify2_bar, test); g_timeout_add(4 * gbinder_servicepoll_interval_ms, test_notify2_foo, test); /* Reusing test_notify_proc */ id = gbinder_servicepoll_add_handler(poll, test_notify_proc, loop); g_assert(id); test_run(&test_opt, loop); g_assert(gbinder_servicepoll_is_known_name(poll, "foo")); g_assert(gbinder_servicepoll_is_known_name(poll, "bar")); g_assert(gbinder_servicepoll_is_known_name(poll, "bar3")); g_assert(!gbinder_servicepoll_is_known_name(poll, "bar1")); g_assert(!gbinder_servicepoll_is_known_name(poll, "bar2")); gbinder_servicepoll_remove_handler(poll, id); gbinder_servicepoll_unref(poll); g_assert(!weakptr); gbinder_servicemanager_unref(manager); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * already_there *==========================================================================*/ static void test_already_there_proc( GBinderServicePoll* poll, const char* name_added, void* user_data) { g_assert(!g_strcmp0(name_added, "foo")); test_quit_later((GMainLoop*)user_data); } static void test_already_there( void) { const char* dev = GBINDER_DEFAULT_BINDER; GBinderIpc* ipc = gbinder_ipc_new(dev, NULL); GMainLoop* loop = g_main_loop_new(NULL, FALSE); GBinderServicePoll* weakptr = NULL; GBinderServiceManager* manager; TestServiceManager* test; GBinderServicePoll* poll; gulong id; test_setup_ping(ipc); manager = gbinder_servicemanager_new(dev); poll = gbinder_servicepoll_new(manager, &weakptr); test = TEST_SERVICEMANAGER(manager); test->services = gutil_strv_add(test->services, "foo"); id = gbinder_servicepoll_add_handler(poll, test_already_there_proc, loop); g_assert(id); test_run(&test_opt, loop); gbinder_servicepoll_remove_handler(poll, id); gbinder_servicepoll_unref(poll); g_assert(!weakptr); gbinder_servicemanager_unref(manager); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, loop); g_main_loop_unref(loop); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_(t) "/servicepoll/" t int main(int argc, char* argv[]) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("basic"), test_basic); g_test_add_func(TEST_("notify1"), test_notify1); g_test_add_func(TEST_("notify2"), test_notify2); g_test_add_func(TEST_("already_there"), test_already_there); test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */ libgbinder-1.1.54/unit/unit_writer/000077500000000000000000000000001526127616700172525ustar00rootroot00000000000000libgbinder-1.1.54/unit/unit_writer/Makefile000066400000000000000000000001161526127616700207100ustar00rootroot00000000000000# -*- Mode: makefile-gmake -*- EXE = unit_writer include ../common/Makefile libgbinder-1.1.54/unit/unit_writer/unit_writer.c000066400000000000000000001566021526127616700220030ustar00rootroot00000000000000/* * Copyright (C) 2018-2026 Slava Monich * Copyright (C) 2025 Jolla Mobile Ltd. * Copyright (C) 2018-2022 Jolla Ltd. * * You may use this file under the terms of the BSD license as follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_common.h" #include "test_binder.h" #include "gbinder_buffer_p.h" #include "gbinder_config.h" #include "gbinder_fmq_p.h" #include "gbinder_local_request_p.h" #include "gbinder_rpc_protocol.h" #include "gbinder_output_data.h" #include "gbinder_reader_p.h" #include "gbinder_writer_p.h" #include "gbinder_ipc.h" #include "gbinder_io.h" #include #include #include #include #include #include static TestOpt test_opt; static const char TMP_DIR_TEMPLATE[] = "gbinder-test-writer-XXXXXX"; static GBinderLocalRequest* test_local_request_new_with_io_dev( const GBinderIo* io, const char *dev) { return gbinder_local_request_new(io, gbinder_rpc_protocol_for_device(dev), NULL); } static GBinderLocalRequest* test_local_request_new_with_io( const GBinderIo* io) { return test_local_request_new_with_io_dev(io, GBINDER_DEFAULT_BINDER); } static GBinderLocalRequest* test_local_request_new() { return test_local_request_new_with_io(&gbinder_io_32); } static GBinderLocalRequest* test_local_request_new_64() { return test_local_request_new_with_io(&gbinder_io_64); } static GBinderLocalRequest* test_local_request_new_64_hidl() { return test_local_request_new_with_io_dev(&gbinder_io_64, GBINDER_DEFAULT_HWBINDER); } /*==========================================================================* * Test context *==========================================================================*/ typedef struct test_context { TestConfig config; char* config_file; } TestContext; static void test_context_init( TestContext* test, const char* prot) { memset(test, 0, sizeof(*test)); test_config_init(&test->config, TMP_DIR_TEMPLATE); if (prot) { char* config = g_strdup_printf("[Protocol]\n" GBINDER_DEFAULT_BINDER " = %s", prot); test->config_file = g_build_filename(test->config.config_dir, "test.conf", NULL); GDEBUG("Config file %s", test->config_file); g_assert(g_file_set_contents(test->config_file, config, -1, NULL)); g_free(config); gbinder_config_file = test->config_file; } } static void test_context_deinit( TestContext* test) { if (test->config_file) { remove(test->config_file); g_free(test->config_file); } test_config_cleanup(&test->config); } /*==========================================================================* * null *==========================================================================*/ static void test_null( void) { GBinderWriter writer; gsize size = 1; gbinder_local_request_init_writer(NULL, &writer); gbinder_writer_append_int8(NULL, 0); gbinder_writer_append_int8(&writer, 0); gbinder_writer_append_int16(NULL, 0); gbinder_writer_append_int16(&writer, 0); gbinder_writer_append_int32(NULL, 0); gbinder_writer_append_int32(&writer, 0); gbinder_writer_append_int64(NULL, 0); gbinder_writer_append_int64(&writer, 0); gbinder_writer_append_float(NULL, 0); gbinder_writer_append_float(&writer, 0); gbinder_writer_append_double(NULL, 0); gbinder_writer_append_double(&writer, 0); gbinder_writer_append_string8(NULL, NULL); gbinder_writer_append_string8(&writer, NULL); gbinder_writer_append_string8_len(NULL, NULL, 0); gbinder_writer_append_string8_len(&writer, NULL, 0); gbinder_writer_append_string16(NULL, NULL); gbinder_writer_append_string16(&writer, NULL); gbinder_writer_append_string16_len(NULL, NULL, 0); gbinder_writer_append_string16_len(&writer, NULL, 0); gbinder_writer_append_string16_utf16(NULL, NULL, 0); gbinder_writer_append_bool(NULL, FALSE); gbinder_writer_append_bool(&writer, FALSE); gbinder_writer_append_fd(NULL, 0); gbinder_writer_append_bytes(NULL, NULL, 0); gbinder_writer_append_bytes(&writer, NULL, 0); gbinder_writer_append_hidl_vec(NULL, NULL, 0, 0); gbinder_writer_append_hidl_string(NULL, NULL); gbinder_writer_append_hidl_string_copy(NULL, NULL); gbinder_writer_append_hidl_string(&writer, NULL); gbinder_writer_append_hidl_string_vec(NULL, NULL, 0); gbinder_writer_append_hidl_string_vec(&writer, NULL, 0); gbinder_writer_append_buffer_object(NULL, NULL, 0); gbinder_writer_append_buffer_object(&writer, NULL, 0); gbinder_writer_append_buffer_object_with_parent(NULL, NULL, 0, NULL); gbinder_writer_append_buffer_object_with_parent(&writer, NULL, 0, NULL); gbinder_writer_append_parcelable(NULL, NULL, 0); gbinder_writer_append_local_object(NULL, NULL); gbinder_writer_append_local_object(&writer, NULL); gbinder_writer_append_remote_object(NULL, NULL); gbinder_writer_append_remote_object(&writer, NULL); gbinder_writer_append_byte_array(NULL, NULL, 0); gbinder_writer_append_byte_array(&writer, NULL, 0); gbinder_writer_add_cleanup(NULL, NULL, 0); gbinder_writer_add_cleanup(NULL, g_free, 0); gbinder_writer_overwrite_int32(NULL, 0, 0); #if GBINDER_FMQ_SUPPORTED gbinder_writer_append_fmq_descriptor(NULL, NULL); gbinder_writer_append_fmq_descriptor(&writer, NULL); #endif g_assert(!gbinder_writer_bytes_written(NULL)); g_assert(!gbinder_writer_get_data(NULL, NULL)); g_assert(!gbinder_writer_get_data(NULL, &size)); g_assert_cmpuint(size, ==, 0); g_assert(!gbinder_output_data_offsets(NULL)); g_assert(!gbinder_output_data_buffers_size(NULL)); g_assert(!gbinder_writer_malloc(NULL, 0)); g_assert(!gbinder_writer_malloc0(NULL, 0)); g_assert(!gbinder_writer_memdup(&writer, NULL, 0)); g_assert(!gbinder_writer_memdup(NULL, &writer, 0)); g_assert(!gbinder_writer_strdup(&writer, NULL)); g_assert(!gbinder_writer_strdup(NULL, "")); } /*==========================================================================* * cleanup *==========================================================================*/ static void test_cleanup_fn( gpointer ptr) { (*((int*)ptr))++; } static void test_cleanup( void) { GBinderLocalRequest* req = test_local_request_new(); GBinderWriter writer; const int value = 42; const char* str = "foo"; int cleanup_count = 0; int* zero; int* copy; char* scopy; gbinder_local_request_init_writer(req, &writer); zero = gbinder_writer_new0(&writer, int); copy = gbinder_writer_memdup(&writer, &value, sizeof(value)); scopy = gbinder_writer_strdup(&writer, str); g_assert_cmpint(*zero, == ,0); g_assert_cmpint(*copy, == ,value); g_assert_cmpstr(scopy, == ,str); gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count); gbinder_writer_add_cleanup(&writer, test_cleanup_fn, &cleanup_count); gbinder_local_request_unref(req); g_assert_cmpint(cleanup_count, == ,2); } /*==========================================================================* * int8 *==========================================================================*/ static void test_int8( void) { const char encoded[] = { 0x80, 0x00, 0x00, 0x00 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int8(&writer, 0x80); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert_cmpuint(data->bytes->len, == ,sizeof(encoded)); g_assert(!memcmp(data->bytes->data, encoded, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * int16 *==========================================================================*/ static void test_int16( void) { const char encoded[] = { TEST_INT16_BYTES(0x80ff), 0x00, 0x00 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int16(&writer, 0x80ff); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert_cmpuint(data->bytes->len, == ,sizeof(encoded)); g_assert(!memcmp(data->bytes->data, encoded, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * int32 *==========================================================================*/ static void test_int32( void) { const guint32 value = 1234567; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); const guint32 value2 = 2345678; gbinder_writer_overwrite_int32(&writer, 0, value2); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value2)); g_assert(!memcmp(data->bytes->data, &value2, data->bytes->len)); // test overlap over the end of the buffer gbinder_writer_overwrite_int32(&writer, 2, value2); g_assert(data->bytes->len == sizeof(value2)); gbinder_local_request_unref(req); } /*==========================================================================* * int64 *==========================================================================*/ static void test_int64( void) { const guint64 value = 12345678; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int64(&writer, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * float *==========================================================================*/ static void test_float( void) { const gfloat value = 12345678; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_float(&writer, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * double *==========================================================================*/ static void test_double( void) { const gdouble value = 12345678; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_double(&writer, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, &value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * bool *==========================================================================*/ static void test_bool( void) { const char encoded[] = { TEST_INT8_BYTES_4(0), 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_bool(&writer, FALSE); gbinder_writer_append_bool(&writer, TRUE); gbinder_writer_append_bool(&writer, 2); /* Will be normalized */ data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(encoded)); g_assert(!memcmp(data->bytes->data, encoded, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * bytes *==========================================================================*/ static void test_bytes( void) { const char value[] = { 0x01, 0x02, 0x03 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_bytes(&writer, value, sizeof(value)); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * string8 *==========================================================================*/ static void test_string8( void) { /* The size of the string is aligned at 4-byte boundary */ static const char value[] = { 't', 'e', 's', 't', 0, 0, 0, 0 }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_string8(&writer, value); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(value)); g_assert(!memcmp(data->bytes->data, value, data->bytes->len)); gbinder_local_request_unref(req); /* NULL string writes nothing */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_string8(&writer, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(!data->bytes->len); gbinder_local_request_unref(req); } /*==========================================================================* * string16 *==========================================================================*/ typedef struct test_string16_data { const char* name; const char* input; const guint8* output; guint output_len; } TestString16Data; static const guint8 string16_tests_data_null[] = { TEST_INT32_BYTES(-1) }; static const guint8 string16_tests_data_empty[] = { TEST_INT32_BYTES(0), 0x00, 0x00, 0xff, 0xff }; static const guint8 string16_tests_data_x[] = { TEST_INT32_BYTES(1), TEST_INT16_BYTES('x'), 0x00, 0x00 }; static const guint8 string16_tests_data_xy[] = { TEST_INT32_BYTES(2), TEST_INT16_BYTES('x'), TEST_INT16_BYTES('y'), 0x00, 0x00, 0x00, 0x00 }; static const guint8 string16_tests_data_surrogates[] = { TEST_INT32_BYTES(8), TEST_INT16_BYTES(0xd83d), TEST_INT16_BYTES(0xde00), TEST_INT16_BYTES(0xd83d), TEST_INT16_BYTES(0xde01), TEST_INT16_BYTES(0xd83d), TEST_INT16_BYTES(0xde02), TEST_INT16_BYTES(0xd83d), TEST_INT16_BYTES(0xde03), 0x00, 0x00, 0x00, 0x00 }; static const TestString16Data test_string16_tests[] = { { "null", NULL, TEST_ARRAY_AND_SIZE(string16_tests_data_null) }, { "empty", "", TEST_ARRAY_AND_SIZE(string16_tests_data_empty) }, { "1", "x", TEST_ARRAY_AND_SIZE(string16_tests_data_x) }, { "2", "xy", TEST_ARRAY_AND_SIZE(string16_tests_data_xy) }, { "surrogates", "\xF0\x9F\x98\x80" "\xF0\x9F\x98\x81" "\xF0\x9F\x98\x82" "\xF0\x9F\x98\x83", TEST_ARRAY_AND_SIZE(string16_tests_data_surrogates) } }; static void test_string16( gconstpointer test_data) { const TestString16Data* test = test_data; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_string16(&writer, test->input); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert_cmpuint(data->bytes->len, == ,test->output_len); g_assert(!memcmp(data->bytes->data, test->output, test->output_len)); gbinder_local_request_unref(req); } /*==========================================================================* * utf16 *==========================================================================*/ typedef struct test_utf16_data { const char* name; const gunichar2* in; gssize in_len; const guint8* out; gssize out_len; } TestUtf16Data; static const guint8 utf16_tests_data_null[] = { TEST_INT32_BYTES(-1) }; static const guint8 utf16_tests_data_empty[] = { TEST_INT32_BYTES(0), 0x00, 0x00, 0xff, 0xff }; static const guint8 utf16_tests_data_x[] = { TEST_INT32_BYTES(1), TEST_INT16_BYTES('x'), 0x00, 0x00 }; static const guint8 utf16_tests_data_xy[] = { TEST_INT32_BYTES(2), TEST_INT16_BYTES('x'), TEST_INT16_BYTES('y'), 0x00, 0x00, 0x00, 0x00 }; static const gunichar2 utf16_tests_input_empty[] = { 0 }; static const gunichar2 utf16_tests_input_x[] = { 'x', 0 }; static const gunichar2 utf16_tests_input_xy[] = { 'x', 'y', 0 }; static const TestUtf16Data test_utf16_tests[] = { { "null", NULL, -1, TEST_ARRAY_AND_SIZE(utf16_tests_data_null) }, { "empty", utf16_tests_input_empty, -1, TEST_ARRAY_AND_SIZE(utf16_tests_data_empty) }, { "1", utf16_tests_input_x, -1, TEST_ARRAY_AND_SIZE(utf16_tests_data_x) }, { "2", utf16_tests_input_xy, 1, TEST_ARRAY_AND_SIZE(utf16_tests_data_x) }, { "3", utf16_tests_input_xy, -1, TEST_ARRAY_AND_SIZE(utf16_tests_data_xy) } }; static void test_utf16( gconstpointer test_data) { const TestUtf16Data* test = test_data; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_string16_utf16(&writer, test->in, test->in_len); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == test->out_len); g_assert(!memcmp(data->bytes->data, test->out, test->out_len)); gbinder_local_request_unref(req); } /*==========================================================================* * hidl_vec *==========================================================================*/ typedef struct test_hidl_vec_data { const char* name; const GBinderIo* io; const void* data; const gsize count; const gsize elemsize; const guint* offsets; guint offsets_count; guint buffers_size; } TestHidlVecData; static guint test_hidl_vec_offsets_32[] = { 0, BUFFER_OBJECT_SIZE_32 }; static guint test_hidl_vec_offsets_64[] = { 0, BUFFER_OBJECT_SIZE_64 }; static const TestHidlVecData test_hidl_vec_tests[] = { { "32/null", &gbinder_io_32, NULL, 0, 0, TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_32), sizeof(GBinderHidlVec) }, { "32/2x1", &gbinder_io_32, "xy", 2, 1, TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_32), sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ }, { "64/null", &gbinder_io_64, NULL, 0, 0, TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_64), sizeof(GBinderHidlVec) }, { "64/2x2", &gbinder_io_64, "xxyy", 2, 2, TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_64), sizeof(GBinderHidlVec) + 8 /* vec data aligned at 8 bytes boundary */ } }; static void test_hidl_vec( gconstpointer test_data) { const TestHidlVecData* test = test_data; GBinderLocalRequest* req = test_local_request_new_with_io(test->io); GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; guint i; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_hidl_vec(&writer, test->data, test->count, test->elemsize); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == test->offsets_count); for (i = 0; i < offsets->count; i++) { g_assert(offsets->data[i] == test->offsets[i]); } g_assert(gbinder_output_data_buffers_size(data) == test->buffers_size); gbinder_local_request_unref(req); } /*==========================================================================* * hidl_string *==========================================================================*/ typedef struct test_hidl_string_data { const char* name; const GBinderIo* io; const char* str; const guint* offsets; guint offsets_count; guint buffers_size; } TestHidlStringData; static guint test_hidl_string_offsets_32[] = { 0, BUFFER_OBJECT_SIZE_32 }; static guint test_hidl_string_offsets_64[] = { 0, BUFFER_OBJECT_SIZE_64 }; static const TestHidlStringData test_hidl_string_tests[] = { { "32/null", &gbinder_io_32, NULL, TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32), sizeof(GBinderHidlString) }, { "32/empty", &gbinder_io_32, "", TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32), sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }, { "32/xxx", &gbinder_io_32, "xxx", TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32), sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }, { "64/null", &gbinder_io_64, NULL, TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64), sizeof(GBinderHidlString) }, { "64/empty", &gbinder_io_64, "", TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64), sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ }, { "64/xxxxxxx", &gbinder_io_64, "xxxxxxx", TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64), sizeof(GBinderHidlString) + 8 /* string data aligned at 8 bytes */ } }; static void test_hidl_string_xxx( const TestHidlStringData* test, void (*append)(GBinderWriter* writer, const char* str)) { GBinderLocalRequest* req = test_local_request_new_with_io(test->io); GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; guint i; gbinder_local_request_init_writer(req, &writer); append(&writer, test->str); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == test->offsets_count); for (i = 0; i < offsets->count; i++) { g_assert(offsets->data[i] == test->offsets[i]); } g_assert(gbinder_output_data_buffers_size(data) == test->buffers_size); gbinder_local_request_unref(req); } static void test_hidl_string( gconstpointer test_data) { test_hidl_string_xxx(test_data, gbinder_writer_append_hidl_string); } static void test_hidl_string_copy( gconstpointer test_data) { test_hidl_string_xxx(test_data, gbinder_writer_append_hidl_string_copy); } static void test_hidl_string2( void) { GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_hidl_string(&writer, "foo"); gbinder_writer_append_hidl_string(&writer, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 4); g_assert(offsets->data[0] == 0); g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_32); g_assert(offsets->data[2] == 2*BUFFER_OBJECT_SIZE_32); /* 2 GBinderHidlStrings + "foo" aligned at 8 bytes boundary */ g_assert(gbinder_output_data_buffers_size(data) == (2 * sizeof(GBinderHidlString) + 8)); gbinder_local_request_unref(req); } /*==========================================================================* * hidl_string_vec *==========================================================================*/ typedef struct test_hidl_string_vec_data { const char* name; const GBinderIo* io; const char** vec; int count; const guint* offsets; guint offsets_count; guint buffers_size; } TestHidlStringVecData; static char* test_hidl_string_vec_data_1[] = { "test" }; static guint test_hidl_string_vec_offsets_empty_32[] = { 0, BUFFER_OBJECT_SIZE_32 }; static guint test_hidl_string_vec_offsets_empty_64[] = { 0, BUFFER_OBJECT_SIZE_64 }; static guint test_hidl_string_vec_offsets_1_32[] = { 0, BUFFER_OBJECT_SIZE_32, 2*BUFFER_OBJECT_SIZE_32 }; static guint test_hidl_string_vec_offsets_1_64[] = { 0, BUFFER_OBJECT_SIZE_64, 2*BUFFER_OBJECT_SIZE_64 }; static const TestHidlStringVecData test_hidl_string_vec_tests[] = { { "32/null", &gbinder_io_32, NULL, -1, TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_empty_32), sizeof(GBinderHidlVec) }, { "32/1", &gbinder_io_32, (const char**)TEST_ARRAY_AND_COUNT(test_hidl_string_vec_data_1), TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_1_32), sizeof(GBinderHidlVec) + sizeof(GBinderHidlString) + 8 }, { "64/null", &gbinder_io_64, NULL, -1, TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_empty_64), sizeof(GBinderHidlVec) }, { "64/1", &gbinder_io_64, (const char**)TEST_ARRAY_AND_COUNT(test_hidl_string_vec_data_1), TEST_ARRAY_AND_COUNT(test_hidl_string_vec_offsets_1_64), sizeof(GBinderHidlVec) + sizeof(GBinderHidlString) + 8 }, }; static void test_hidl_string_vec( gconstpointer test_data) { const TestHidlStringVecData* test = test_data; GBinderLocalRequest* req = test_local_request_new_with_io(test->io); GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; guint i; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_hidl_string_vec(&writer, (const char**)test->vec, test->count); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == test->offsets_count); for (i = 0; i < offsets->count; i++) { g_assert(offsets->data[i] == test->offsets[i]); } g_assert(gbinder_output_data_buffers_size(data) == test->buffers_size); gbinder_local_request_unref(req); } /*==========================================================================* * buffer *==========================================================================*/ static void test_buffer( void) { GBinderLocalRequest* req = test_local_request_new(); guint32 x1 = 1; guint32 x2 = 2; GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_buffer_object(&writer, &x1, sizeof(x1)); gbinder_writer_append_buffer_object(&writer, &x2, sizeof(x2)); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_32); /* Each buffer is aligned at 8 bytes boundary */ g_assert(gbinder_output_data_buffers_size(data) == 16); gbinder_local_request_unref(req); } /*==========================================================================* * parent *==========================================================================*/ typedef struct test_data { guint32 x; } TestData; typedef struct test_data_pointer { TestData* ptr; } TestDataPointer; static void test_parent( void) { GBinderLocalRequest* req = test_local_request_new(); TestData test_data; TestDataPointer test; GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; GBinderParent parent; test_data.x = 1; test.ptr = &test_data; gbinder_local_request_init_writer(req, &writer); parent.index = gbinder_writer_append_buffer_object(&writer, &test, sizeof(test)); parent.offset = 0; g_assert(parent.index == 0); gbinder_writer_append_buffer_object_with_parent(&writer, &test_data, sizeof(test_data), &parent); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 2); g_assert(offsets->data[0] == 0); g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_32); /* Each buffer is aligned at 8 bytes boundary */ g_assert(gbinder_output_data_buffers_size(data) == 16); gbinder_local_request_unref(req); } /*==========================================================================* * parcelable *==========================================================================*/ static void test_parcelable( void) { const guint8 encoded_non_null[] = { TEST_INT32_BYTES(42), TEST_INT32_BYTES(1), TEST_INT32_BYTES(sizeof(TestData) + sizeof(gint32)), TEST_INT32_BYTES(10) }; const gint32 prefix = 42; const gint32 test_null_value = 0; GBinderLocalRequest* req; GBinderOutputData* data; GBinderWriter writer, parcelable; TestData test_data; test_data.x = 10; /* Non-null */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, prefix); gbinder_writer_append_parcelable(&writer, &test_data, sizeof(test_data)); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(encoded_non_null)); g_assert(!memcmp(data->bytes->data, &encoded_non_null, data->bytes->len)); gbinder_local_request_unref(req); /* Same with start/finish parcelable */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_int32(&writer, prefix); gbinder_writer_start_parcelable(&writer, &parcelable); gbinder_writer_append_bytes(&parcelable, &test_data, sizeof(test_data)); gbinder_writer_finish_parcelable(&parcelable); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(encoded_non_null)); g_assert(!memcmp(data->bytes->data, &encoded_non_null, data->bytes->len)); gbinder_local_request_unref(req); /* Null */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_parcelable(&writer, NULL, 0); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(test_null_value)); g_assert(!memcmp(data->bytes->data, &test_null_value, data->bytes->len)); gbinder_local_request_unref(req); } /*==========================================================================* * struct *==========================================================================*/ typedef struct test_struct_data { int x; GBinderHidlString str1; GBinderHidlString str2; GBinderHidlVec vec; /* vec */ } TestStruct; static void test_struct( void) { static const GBinderWriterType test_data_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(TestData), NULL }; static const GBinderWriterField test_data_pointer_f[] = { GBINDER_WRITER_FIELD_POINTER(TestDataPointer,ptr, &test_data_t), GBINDER_WRITER_FIELD_END() }; static const GBinderWriterField test_struct_f[] = { GBINDER_WRITER_FIELD_HIDL_STRING(TestStruct,str1), GBINDER_WRITER_FIELD_HIDL_STRING(TestStruct,str2), GBINDER_WRITER_FIELD_HIDL_VEC(TestStruct, vec, &test_data_t), GBINDER_WRITER_FIELD_END() }; static const GBinderWriterType test_struct_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(TestStruct), test_struct_f }; static const GBinderWriterType test_data_pointer_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(TestDataPointer), test_data_pointer_f }; static const GBinderWriterField test_struct_vec_f[] = { { "vec", GBINDER_HIDL_VEC_BUFFER_OFFSET, &test_struct_t, gbinder_writer_field_hidl_vec_write_buf, NULL }, GBINDER_WRITER_FIELD_END() }; static const GBinderWriterType test_struct_vec_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(GBinderHidlVec), test_struct_vec_f }; /* Vector with no type information is handled as empty vector */ static const GBinderWriterField test_struct_vec2_f[] = { { "vec", GBINDER_HIDL_VEC_BUFFER_OFFSET, NULL, gbinder_writer_field_hidl_vec_write_buf, NULL }, GBINDER_WRITER_FIELD_END() }; static const GBinderWriterType test_struct_vec2_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(GBinderHidlVec), test_struct_vec2_f }; GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GBinderWriter writer; GUtilIntArray* offsets; TestData test_data; TestDataPointer test_data_ptr; TestStruct test_struct; GBinderHidlVec vec; test_data.x = 42; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &test_data, &test_data_t, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,1); g_assert_cmpuint(offsets->data[0], == ,0); /* Buffers are aligned at 8 bytes boundary */ g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,8); gbinder_local_request_unref(req); /* Write TestStruct */ memset(&test_struct, 0, sizeof(test_struct)); test_struct.x = 42; test_struct.str1.data.str = "test"; test_struct.str1.len = strlen(test_struct.str1.data.str); test_struct.vec.data.ptr = &test_data; test_struct.vec.count = 1; req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &test_struct, &test_struct_t, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,4); g_assert_cmpuint(offsets->data[0], == ,0); gbinder_local_request_unref(req); /* Write TestDataPointer */ test_data_ptr.ptr = &test_data; req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &test_data_ptr, &test_data_pointer_t, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,2); gbinder_local_request_unref(req); /* Write vec */ memset(&vec, 0, sizeof(vec)); vec.data.ptr = &test_struct; vec.count = 1; req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &vec, &test_struct_vec_t, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,5); g_assert_cmpuint(offsets->data[0], == ,0); gbinder_local_request_unref(req); /* Write vec in a different way */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct_vec(&writer, &test_struct, 1, &test_struct_t); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,5); g_assert_cmpuint(offsets->data[0], == ,0); gbinder_local_request_unref(req); /* Corner cases */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &vec, NULL, NULL); /* Without the type information, an empty buffer is written */ data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,1); g_assert_cmpuint(offsets->data[0], == ,0); gbinder_local_request_unref(req); /* Vector without type information */ req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &vec, &test_struct_vec2_t, NULL); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,2); g_assert_cmpuint(offsets->data[0], == ,0); gbinder_local_request_unref(req); } /*==========================================================================* * struct_vec *==========================================================================*/ static void test_struct_vec( void) { /* vec */ static const GBinderWriterField vec_byte_ptr_f[] = { { "ptr", GBINDER_HIDL_VEC_BUFFER_OFFSET, &gbinder_writer_type_byte, gbinder_writer_field_hidl_vec_write_buf, NULL }, GBINDER_WRITER_FIELD_END() }; static const GBinderWriterType vec_byte_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(GBinderHidlVec), vec_byte_ptr_f }; /* vec */ static const GBinderWriterField vec_int32_ptr_f[] = { { "ptr", GBINDER_HIDL_VEC_BUFFER_OFFSET, &gbinder_writer_type_int32, gbinder_writer_field_hidl_vec_write_buf, NULL }, GBINDER_WRITER_FIELD_END() }; static const GBinderWriterType vec_int32_t = { GBINDER_WRITER_STRUCT_NAME_AND_SIZE(GBinderHidlVec), vec_int32_ptr_f }; GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL); GBinderLocalRequest* req = gbinder_local_request_new(gbinder_ipc_io(ipc), gbinder_ipc_protocol(ipc), NULL); GBinderOutputData* writer_data; GBinderReaderData reader_data; GBinderWriter writer; GBinderReader reader; GUtilIntArray* offsets; GBinderHidlVec vec_byte, vec_int32; gsize count, elemsize; const void* vec_data; guint i; static const guint8 vec_byte_data[] = { 0x01, 0x02 }; static const guint32 vec_int32_data[] = { 42 }; memset(&vec_byte, 0, sizeof(vec_byte)); vec_byte.data.ptr = vec_byte_data; vec_byte.count = G_N_ELEMENTS(vec_byte_data); memset(&vec_int32, 0, sizeof(vec_int32)); vec_int32.data.ptr = vec_int32_data; vec_int32.count = G_N_ELEMENTS(vec_int32_data); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_struct(&writer, &vec_byte, &vec_byte_t, NULL); gbinder_writer_append_struct(&writer, &vec_int32, &vec_int32_t, NULL); writer_data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(writer_data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,4); /* Set up the reader */ memset(&reader_data, 0, sizeof(reader_data)); reader_data.reg = gbinder_ipc_object_registry(ipc); reader_data.objects = g_new0(void*, offsets->count + 1); reader_data.buffer = gbinder_buffer_new(ipc->driver, gutil_memdup(writer_data->bytes->data, writer_data->bytes->len), writer_data->bytes->len, reader_data.objects); for (i = 0; i < offsets->count; i++) { reader_data.objects[i] = reader_data.buffer->data + offsets->data[i]; } /* Read those vectors back */ gbinder_reader_init(&reader, &reader_data, 0, writer_data->bytes->len); /* vec */ vec_data = gbinder_reader_read_hidl_vec(&reader, &count, &elemsize); g_assert(vec_data); g_assert_cmpuint(count, == ,G_N_ELEMENTS(vec_byte_data)); g_assert_cmpuint(elemsize, == ,sizeof(vec_byte_data[0])); g_assert(!memcmp(vec_data, vec_byte_data, sizeof(vec_byte_data))); /* vec */ vec_data = gbinder_reader_read_hidl_vec(&reader, &count, &elemsize); g_assert(vec_data); g_assert_cmpuint(count, == ,G_N_ELEMENTS(vec_int32_data)); g_assert_cmpuint(elemsize, == ,sizeof(vec_int32_data[0])); g_assert(!memcmp(vec_data, vec_int32_data, sizeof(vec_int32_data))); gbinder_buffer_free(reader_data.buffer); gbinder_local_request_unref(req); gbinder_ipc_unref(ipc); test_binder_exit_wait(&test_opt, NULL); } /*==========================================================================* * fd * fd_invalid *==========================================================================*/ static void test_fd2( int fd) { GBinderLocalRequest* req = test_local_request_new(); GBinderOutputData* data; GUtilIntArray* offsets; GBinderWriter writer; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_fd(&writer, fd); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert(offsets->count == 1); g_assert(offsets->data[0] == 0); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); gbinder_local_request_unref(req); } static void test_fd( void) { test_fd2(0); } static void test_fd_invalid( void) { test_fd2(-1); } /*==========================================================================* * fd_close_error *==========================================================================*/ static void test_fd_close_error( void) { const GBinderIo* io = &gbinder_io_32; GBinderLocalRequest* req = test_local_request_new_with_io(io); GBinderOutputData* data; GBinderWriter writer; int fd = -1; gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_fd(&writer, STDOUT_FILENO); data = gbinder_local_request_data(req); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); /* Fetch duplicated fd and close it. That makes the second close * done by gbinder_writer_data_close_fd() fail. */ g_assert(io->decode_fd_object(data->bytes->data, data->bytes->len, &fd)); g_assert(close(fd) == 0); gbinder_local_request_unref(req); } /*==========================================================================* * local_object *==========================================================================*/ typedef struct test_local_object_data { const char* name; const char* protocol; const guint objsize; } TestLocalObjectData; static const TestLocalObjectData local_object_tests [] = { { "default", NULL, BINDER_OBJECT_SIZE_32 }, { "aidl", "aidl", BINDER_OBJECT_SIZE_32 }, { "aidl2", "aidl2", BINDER_OBJECT_SIZE_32 }, { "aidl3", "aidl3", BINDER_OBJECT_SIZE_32 + 4 } }; static void test_local_object( gconstpointer test_data) { const TestLocalObjectData* test = test_data; GBinderLocalRequest* req; GBinderOutputData* data; GBinderWriter writer; TestContext context; test_context_init(&context, test->protocol); req = test_local_request_new(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_local_object(&writer, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,0); g_assert_cmpuint(data->bytes->len, == ,test->objsize); gbinder_local_request_unref(req); test_context_deinit(&context); } /*==========================================================================* * remote_object *==========================================================================*/ static void test_remote_object( void) { GBinderLocalRequest* req = test_local_request_new_64(); GBinderOutputData* data; GBinderWriter writer; TestContext test; test_context_init(&test, NULL); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_remote_object(&writer, NULL); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == BINDER_OBJECT_SIZE_64); gbinder_local_request_unref(req); test_context_deinit(&test); } /*==========================================================================* * byte_array *==========================================================================*/ static void test_byte_array( void) { GBinderLocalRequest* req; GBinderOutputData* data; GBinderWriter writer; const char in_data[] = "abcd12"; gint32 in_len = sizeof(in_data) - 1; gint32 null_len = -1; /* test for NULL byte array with non-zero len */ req = test_local_request_new_64(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_byte_array(&writer, NULL, 42); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(gint32)); g_assert(!memcmp(data->bytes->data, &null_len, data->bytes->len)); gbinder_local_request_unref(req); /* test for valid array with zero len */ req = test_local_request_new_64(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_byte_array(&writer, in_data, 0); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(gint32)); g_assert(!memcmp(data->bytes->data, &null_len, data->bytes->len)); gbinder_local_request_unref(req); /* test for valid array with correct len */ req = test_local_request_new_64(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_byte_array(&writer, in_data, in_len); data = gbinder_local_request_data(req); g_assert(!gbinder_output_data_offsets(data)); g_assert(!gbinder_output_data_buffers_size(data)); g_assert(data->bytes->len == sizeof(in_len) + G_ALIGN4(in_len)); g_assert(!memcmp(data->bytes->data, &in_len, sizeof(in_len))); g_assert(!memcmp(data->bytes->data + sizeof(in_len), in_data, in_len)); gbinder_local_request_unref(req); } /*==========================================================================* * fmq descriptor *==========================================================================*/ #if GBINDER_FMQ_SUPPORTED static void test_fmq_descriptor( void) { GBinderLocalRequest* req; GBinderOutputData* data; GUtilIntArray* offsets; GBinderWriter writer; const gint32 len = 3 * BUFFER_OBJECT_SIZE_64 /* Buffer objects */ + sizeof(gint64) /* gint64 */ + 4 * sizeof(gint64); /* binder_fd_array_object */ GBinderFmq* fmq = gbinder_fmq_new(sizeof(guint32), 5, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); g_assert(fmq); req = test_local_request_new_64_hidl(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_fmq_descriptor(&writer, fmq); data = gbinder_local_request_data(req); offsets = gbinder_output_data_offsets(data); g_assert(offsets); g_assert_cmpuint(offsets->count, == ,4); g_assert(offsets->data[0] == 0); g_assert(offsets->data[1] == BUFFER_OBJECT_SIZE_64); g_assert(offsets->data[2] == 2 * BUFFER_OBJECT_SIZE_64 + sizeof(gint64)); g_assert(offsets->data[3] == 3 * BUFFER_OBJECT_SIZE_64 + sizeof(gint64)); g_assert_cmpuint(data->bytes->len, == ,len); gbinder_local_request_unref(req); gbinder_fmq_unref(fmq); } static void test_fmq_descriptor_aidl( void) { GBinderLocalRequest* req; GBinderOutputData* data; GBinderWriter writer; const gint32 len = 2 * sizeof(gint32) /* FMQ decriptor parcelable header */ + sizeof(gint32) + 4 * (4 * sizeof(gint32) + sizeof(gint64)) /* grantors + parcelable headers */ + 2 * sizeof(gint32) /* Native handle parcelable header */ + sizeof(gint32) + 2 * sizeof(gint32) + 3 * sizeof(gint64) /* fds (1 fd array) */ + sizeof(gint32) /* ints (0 int array) */ + sizeof(gint32) /* quantum */ + sizeof(gint32); /* flags */ GBinderFmq* fmq = gbinder_fmq_new(sizeof(guint32), 5, GBINDER_FMQ_TYPE_SYNC_READ_WRITE, GBINDER_FMQ_FLAG_CONFIGURE_EVENT_FLAG, -1, 0); g_assert(fmq); req = test_local_request_new_64(); gbinder_local_request_init_writer(req, &writer); gbinder_writer_append_fmq_descriptor(&writer, fmq); data = gbinder_local_request_data(req); g_assert_cmpuint(data->bytes->len, == ,len); gbinder_local_request_unref(req); gbinder_fmq_unref(fmq); } #endif /* GBINDER_FMQ_SUPPORTED */ /*==========================================================================* * bytes_written *==========================================================================*/ static void test_bytes_written( void) { const guint32 value = 1234567; GBinderLocalRequest* req = test_local_request_new(); GBinderWriter writer; const void* data; gsize size = 0; gbinder_local_request_init_writer(req, &writer); g_assert(gbinder_writer_bytes_written(&writer) == 0); gbinder_writer_append_int32(&writer, value); g_assert_cmpuint(gbinder_writer_bytes_written(&writer), == ,sizeof(value)); data = gbinder_writer_get_data(&writer, NULL); g_assert(data); g_assert(data == gbinder_writer_get_data(&writer, &size)); g_assert_cmpuint(size, == ,sizeof(value)); g_assert(!memcmp(data, &value, size)); gbinder_local_request_unref(req); } /*==========================================================================* * Common *==========================================================================*/ #define TEST_PREFIX "/writer/" #define TEST_(t) TEST_PREFIX t int main(int argc, char* argv[]) { guint i; g_test_init(&argc, &argv, NULL); g_test_add_func(TEST_("null"), test_null); g_test_add_func(TEST_("cleanup"), test_cleanup); g_test_add_func(TEST_("int8"), test_int8); g_test_add_func(TEST_("int16"), test_int16); g_test_add_func(TEST_("int32"), test_int32); g_test_add_func(TEST_("int64"), test_int64); g_test_add_func(TEST_("float"), test_float); g_test_add_func(TEST_("double"), test_double); g_test_add_func(TEST_("bool"), test_bool); g_test_add_func(TEST_("bytes"), test_bytes); g_test_add_func(TEST_("string8"), test_string8); for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) { const TestString16Data* test = test_string16_tests + i; char* path = g_strconcat(TEST_("string16/"), test->name, NULL); g_test_add_data_func(path, test, test_string16); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_utf16_tests); i++) { const TestUtf16Data* test = test_utf16_tests + i; char* path = g_strconcat(TEST_("utf16/"), test->name, NULL); g_test_add_data_func(path, test, test_utf16); g_free(path); } for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) { const TestHidlVecData* test = test_hidl_vec_tests + i; char* path = g_strconcat(TEST_("hidl_vec/"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_vec); g_free(path); } g_test_add_func(TEST_("hidl_string/2strings"), test_hidl_string2); for (i = 0; i < G_N_ELEMENTS(test_hidl_string_tests); i++) { const TestHidlStringData* test = test_hidl_string_tests + i; char* path = g_strconcat(TEST_("hidl_string/"), test->name, NULL); char* path2 = g_strconcat(TEST_("hidl_string_copy/"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_string); g_test_add_data_func(path2, test, test_hidl_string_copy); g_free(path); g_free(path2); } for (i = 0; i < G_N_ELEMENTS(test_hidl_string_vec_tests); i++) { const TestHidlStringVecData* test = test_hidl_string_vec_tests + i; char* path = g_strconcat(TEST_("hidl_string_vec/"), test->name, NULL); g_test_add_data_func(path, test, test_hidl_string_vec); g_free(path); } g_test_add_func(TEST_("buffer"), test_buffer); g_test_add_func(TEST_("parent"), test_parent); g_test_add_func(TEST_("parcelable"), test_parcelable); g_test_add_func(TEST_("struct"), test_struct); g_test_add_func(TEST_("struct_vec"), test_struct_vec); g_test_add_func(TEST_("fd"), test_fd); g_test_add_func(TEST_("fd_invalid"), test_fd_invalid); g_test_add_func(TEST_("fd_close_error"), test_fd_close_error); for (i = 0; i < G_N_ELEMENTS(local_object_tests); i++) { const TestLocalObjectData* test = local_object_tests + i; char* path = g_strconcat(TEST_("local_object/"), test->name, NULL); g_test_add_data_func(path, test, test_local_object); g_free(path); } g_test_add_func(TEST_("remote_object"), test_remote_object); g_test_add_func(TEST_("byte_array"), test_byte_array); g_test_add_func(TEST_("bytes_written"), test_bytes_written); #if GBINDER_FMQ_SUPPORTED { int test_fd = syscall(__NR_memfd_create, "test", MFD_CLOEXEC); if (test_fd < 0 && errno == ENOSYS) { GINFO("Skipping tests that rely on memfd_create"); } else { close(test_fd); g_test_add_func(TEST_("fmq_descriptor"), test_fmq_descriptor); g_test_add_func(TEST_("fmq_descriptor_aidl"), test_fmq_descriptor_aidl); } } #endif /* GBINDER_FMQ_SUPPORTED */ test_init(&test_opt, argc, argv); return g_test_run(); } /* * Local Variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */