Compare commits

...

44 Commits

Author SHA1 Message Date
Slava Monich
80e9be5343 Version 1.1.3 2020-12-23 21:50:41 +02:00
Slava Monich
d6d6b76fa5 [unit] Added unit_servicemanager_aidl2 test. JB#42956 2020-12-23 21:44:53 +02:00
Slava Monich
d18a352a82 [gbinder] Set function attributes. JB#42956
For better compile time diagnostics.
2020-12-23 21:43:28 +02:00
Slava Monich
5fbaabb47e [unit] Improved gbinder_servicemanager_aidl coverage. JB#42956
Also made ServiceManagerAidl a GBinderLocalObject-derived class to prepare
for possibly making it (deriving classes from GBinderLocalObject) a public
functionality.
2020-12-23 21:38:17 +02:00
Slava Monich
83cc13b817 [gbinder] Fixed some typos. JB#42956 2020-12-23 21:37:46 +02:00
Slava Monich
1679cda4c5 [gbinder] Allow to derive objects from GBinderLocalObject. JB#42956
It's something I've always had in mind. It's still an internal API
but it's quite likely that at some point it will be made public.
For now it's only going to be used by unit tests.
2020-12-23 18:17:51 +02:00
Slava Monich
e5f11aafc1 Version 1.1.2 2020-12-23 12:40:10 +02:00
Slava Monich
f262c77b17 Merge pull request #58 from monich/unit-wait
Fix random unit text failures
2020-12-23 12:29:35 +02:00
Slava Monich
3ef00effc5 [unit] Call gbinder_ipc_exit() when appropriate. Fixes JB#52595
If a test calls test_binder_set_looper_enabled(fd, TRUE) to enable
processing of incoming data by the looper thread, the same thread
may get picked up by the next test and swallow the reply before the
transaction (for which the reply was intended) has been submitted.
Which may cause that next test to either fail or (if the transaction
was synchronous) block forever, stalling the build.

Calling gbinder_ipc_exit() makes sure that looper thread terminates
before the next test starts.
2020-12-22 23:50:37 +02:00
Slava Monich
1ce13bea91 [unit] Speed up test_binder_exit_wait()
Use condition variable to signal closure of the last descriptor.
2020-12-22 23:40:56 +02:00
Slava Monich
eaab366dcb Version 1.1.1 2020-12-22 15:19:03 +02:00
Slava Monich
784f06c415 Merge pull request #57 from monich/sm-test
A few minor tweaks and another unit test
2020-12-22 15:13:09 +02:00
Slava Monich
4b07e80a8f [gbinder] Added couple more looper checks
We must have looper running in order to receive death notifications.
2020-12-19 01:42:42 +02:00
Slava Monich
9f7fac407d [unit] Added unit_servicemanager_aidl. JB#42956
And refactored binder simulator, it can now simulate a lot
more sophisticated scenarious.
2020-12-08 05:03:15 +02:00
Slava Monich
47b6668876 [gbinder] Added internal gbinder_ipc_set_max_threads(). JB#42956
It's needed by unit tests to make sure that asynchronous requests
get executed sequentially. Our binder simulation is too primitive
to handle truely overlapping requests.
2020-12-08 05:02:57 +02:00
Slava Monich
951977961b [gbinder] Pass 0x0f priority to service list request. JB#42956
Based on examination of servicemanager source code, it seems to be
a better idea because it's interpreted as a bitmask.
2020-12-07 18:56:04 +02:00
Slava Monich
f069c3a595 [gbinder] Corner cases for abandoned loopers. JB#42956
If the last GBinderIpc reference was dropped by the looper thread,
the exit flag wasn't set by gbinder_ipc_looper_stop() and such
looper thread kept hanging forever. Also, that flag wasn't checked
by gbinder_driver_read(), and even though it's even less likely
to cause any trouble in real life, let's fix that too.
2020-12-07 05:19:14 +02:00
Slava Monich
b84194e8c7 Version 1.1.0 2020-12-04 13:50:42 +02:00
Slava Monich
e06e989aa5 [gbinder] Updated README 2020-12-04 13:44:41 +02:00
Slava Monich
ab75505437 Merge pull request #56 from monich/config
Make protocol and service managers configurable per binder device
2020-12-04 13:33:10 +02:00
Slava Monich
e97663b58e [gbinder] Added support for API level presets. JB#51844
API level can be configured via libgbinder config file like this:

  [General]
  ApiLevel = 28

and that adds protocol and servicemanager values appropriate for
the specified Android API level.

Presets are currently defined for API levels 28 and 29.
2020-12-04 03:09:37 +02:00
Slava Monich
a92f57a30c [gbinder] Added "aidl2" variant of RPC protocol. JB#51844
It first appeared in Android 10 (API level 29)
2020-12-03 20:57:09 +02:00
Slava Monich
ebb9381e46 [gbinder] Added "aidl2" variant of service manager. JB#51844
It first appeared in Android 9 (API level 28)
2020-12-03 20:26:50 +02:00
Slava Monich
5fb441fb81 [gbinder] Support for multiple config files. JB#51844
In additions to loading /etc/gbinder.conf, directory /etc/gbinder.d is
searched for .conf files and those overwrite the configuration found
in /etc/gbinder.conf (or more likely, provide entries of their own)
2020-12-03 20:25:21 +02:00
Slava Monich
f3a00fe8a2 [gbinder] Made service managers configurable. JB#51844
Custom device-to-protocol mapping can be provided via /etc/gbinder.conf
and the default config looks like this:

  [ServiceManager]
  Default = aidl
  /dev/binder = aidl
  /dev/hwbinder = hidl

gbinder_defaultservicemanager_new() and gbinder_hwservicemanager_new()
have beed deprecated and should no longer be used. They will keep
returning the same original servicemanagers no matter what's specified
in the configuration files.

Those calls should be replaced with gbinder_servicemanager_new() which
picks the right servicemanager implementation for the specified device.
2020-12-03 20:22:06 +02:00
Slava Monich
573494570c [gbinder] Renamed service managers. JB#51844
It's become clear that more of those are coming, consistent naming
scheme is needed. For now it's just this:

  defaultservicemanager => servicemanager_aidl
  hwservicemanager => servicemanager_hidl
2020-12-03 20:15:47 +02:00
Slava Monich
92392f5c9a [gbinder] Made RPC protocol configurable per device. JB#51844
Custom device-to-protocol mapping can be provided via /etc/gbinder.conf
and the default config looks like this:

  [Protocol]
  Default = aidl
  /dev/binder = aidl
  /dev/hwbinder = hidl
2020-12-03 20:15:33 +02:00
Slava Monich
8a634a0d95 Version 1.0.47 2020-09-19 20:17:50 +03:00
Slava Monich
23c5d48b5d Merge pull request #55 from rinigus/permissions
Install library as executable
2020-09-19 20:12:01 +03:00
Rinigus
f000b344dd [packaging] Install library as executable. JB#51013
Installs the library with exe permissions, as expected on RPM systems.

Signed-off-by: Rinigus <rinigus.git@gmail.com>
2020-09-19 20:05:37 +03:00
Slava Monich
42177e1ff2 Version 1.0.46 2020-07-31 02:06:28 +03:00
Slava Monich
091ca483e7 Merge pull request #52 from monich/wakeup_err
WARNING! Failed to wake up the looper
2020-07-31 02:01:13 +03:00
Slava Monich
42ce4f8821 Merge pull request #54 from monich/tools
Package binder-list and binder-ping utilities
2020-07-31 01:59:42 +03:00
Slava Monich
9a1781765b [packaging] Package binder-list and binder-ping tools. JB#42956
Package those as a separate package which won't be getting installed
by default, but will be easy enough to pull in, if needed. They are
quite useful as development tools.
2020-07-31 01:28:51 +03:00
Slava Monich
544b8181e5 [binder-list] Use /dev/hwbinder by default. JB#42956
It's become far more common use case recently.
2020-07-31 00:42:35 +03:00
Slava Monich
82ea37ead9 Version 1.0.45 2020-07-27 23:20:41 +03:00
Slava Monich
882ef93084 Merge pull request #53 from monich/null_vec
Fix long-standing issue with NULL vectors
2020-07-27 22:45:40 +03:00
Slava Monich
881469ab7f [gbinder] Always provide data buffer for hidl vector. JB#42956
Every vector, even the one without data, requires two buffer objects.
That's what generated stubs expect.
2020-07-27 20:53:01 +03:00
Slava Monich
d7834650ae [gbinder] Make sure we drop fds that are going to be closed. JB#50224
Even if for whatever reason the transaction thread doesn't get woken up
by the transaction handler, it (the transaction thread) still needs to
drop the transaction pipe fds unless all other references have already
been dropped. Otherwise those fds are going to be closed by whoever
drops the last reference to GBinderIpcLooperTx and become invalid.
2020-07-16 17:45:17 +03:00
Slava Monich
20bf183a10 [gbinder] Better diagnostics for polling failures. JB#50224
Something fishy is going on sometimes, which results in transaction
pipe descriptors being closed and yet still being used. Let's see
if this sheds any light on it.
2020-07-09 01:40:16 +03:00
Slava Monich
c990ac66da Version 1.0.44 2020-06-29 17:07:25 +03:00
Slava Monich
0964163a35 Merge pull request #51 from monich/jb49615
Never drop valid incoming transactions
2020-06-29 17:01:08 +03:00
Slava Monich
fc980b1bc9 [unit] Added GBinderIpc tests. JB#49615
Simulate simultaneous two-way transactions.
2020-06-26 16:10:22 +03:00
Slava Monich
e76c8a3ba7 [gbinder] Never drop valid incoming transactions. Fixes JB#49615
Incoming transactions were being rejected if they were received
while we were waiting for completion of our own transaction.
That wasn't right.
2020-06-26 16:09:17 +03:00
57 changed files with 4533 additions and 717 deletions

6
.gitignore vendored
View File

@@ -1,11 +1,13 @@
*~
debian/files
debian/libgbinder-dev
debian/libgbinder
debian/libgbinder-dev
debian/libgbinder-tools
debian/*.debhelper.log
debian/*.debhelper
debian/*.substvars
debian/*.install
debian/libgbinder.install
debian/libgbinder-dev.install
debian/tmp
documentation.list
installroot

View File

@@ -15,8 +15,8 @@
#
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_RELEASE = 43
VERSION_MINOR = 1
VERSION_RELEASE = 3
# Version for pkg-config
PCVERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
@@ -78,6 +78,7 @@ SRC = \
gbinder_buffer.c \
gbinder_cleanup.c \
gbinder_client.c \
gbinder_config.c \
gbinder_driver.c \
gbinder_eventloop.c \
gbinder_io_32.c \
@@ -97,9 +98,10 @@ SRC = \
gbinder_writer.c
SRC += \
gbinder_defaultservicemanager.c \
gbinder_hwservicemanager.c \
gbinder_servicemanager.c
gbinder_servicemanager.c \
gbinder_servicemanager_aidl.c \
gbinder_servicemanager_aidl2.c \
gbinder_servicemanager_hidl.c
SRC += \
gbinder_system.c
@@ -231,7 +233,7 @@ clean:
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/*.install
rm -f debian/libgbinder.install debian/libgbinder-dev.install
test:
make -C unit test
@@ -319,7 +321,7 @@ INSTALL_INCLUDE_DIR = $(DESTDIR)/usr/include/$(NAME)
INSTALL_PKGCONFIG_DIR = $(DESTDIR)$(ABS_LIBDIR)/pkgconfig
install: $(INSTALL_LIB_DIR)
$(INSTALL_FILES) $(RELEASE_SO) $(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)

39
README
View File

@@ -1,7 +1,44 @@
GLib-style interface to binder (Android IPC mechanism)
Provides:
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 transation 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 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, one can specify the desired Android API level:
[General]
ApiLevel = 29
and let libgbinder pick the appropriate preset.

58
debian/changelog vendored
View File

@@ -1,3 +1,61 @@
libgbinder (1.1.3) unstable; urgency=low
* Improved unit test coverage
-- Slava Monich <slava.monich@jolla.com> Wed, 23 Dec 2020 21:48:27 +0200
libgbinder (1.1.2) unstable; urgency=low
* Fixed random unit text failures
-- Slava Monich <slava.monich@jolla.com> Wed, 23 Dec 2020 12:39:22 +0200
libgbinder (1.1.1) unstable; urgency=low
* Handle corner cases for abandoned loopers
* Pass 0x0f priority to aidl2 service list request.
* Improved binder simulation for unit tests
* Added servicemanager_aidl unit test
-- Slava Monich <slava.monich@jolla.com> Tue, 22 Dec 2020 15:15:10 +0200
libgbinder (1.1.0) unstable; urgency=low
* Made RPC protocol configurable per binder device
* 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 <slava.monich@jolla.com> Fri, 04 Dec 2020 13:47:26 +0200
libgbinder (1.0.47) unstable; urgency=low
* Make library executable on RPM based systems
-- Slava Monich <slava.monich@jolla.com> 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 <slava.monich@jolla.com> Fri, 31 Jul 2020 02:04:38 +0300
libgbinder (1.0.45) unstable; urgency=low
* Always provide data buffer for hidl vector
-- Slava Monich <slava.monich@jolla.com> Mon, 27 Jul 2020 23:19:25 +0300
libgbinder (1.0.44) unstable; urgency=low
* Never drop valid incoming transactions
-- Slava Monich <slava.monich@jolla.com> Mon, 29 Jun 2020 17:05:22 +0300
libgbinder (1.0.43) unstable; urgency=low
* Hide internal symbols

6
debian/control vendored
View File

@@ -16,3 +16,9 @@ Section: libdevel
Architecture: any
Depends: libgbinder (= ${binary:Version}), ${misc:Depends}
Description: Development files for libgbinder
Package: libgbinder-tools
Section: utils
Architecture: any
Depends: libgbinder, ${misc:Depends}
Description: Binder command line utilities

1
debian/libgbinder-tools.install vendored Normal file
View File

@@ -0,0 +1 @@
debian/tmp/usr/bin/* usr/bin

4
debian/rules vendored
View File

@@ -8,9 +8,13 @@ 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 test/binder-list release
dh_auto_build -- -C test/binder-ping release
override_dh_auto_install:
dh_auto_install -- LIBDIR=$(LIBDIR) install-dev
dh_auto_install -- -C test/binder-list
dh_auto_install -- -C test/binder-ping
%:
dh $@

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -80,25 +80,29 @@ gbinder_servicemanager_new(
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev);
const char* dev)
G_DEPRECATED_FOR(gbinder_servicemanager_new);
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev);
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);
void* user_data)
G_GNUC_WARN_UNUSED_RESULT;
GBinderLocalObject*
gbinder_servicemanager_new_local_object2(
GBinderServiceManager* sm,
const char* const* ifaces,
GBinderLocalTransactFunc handler,
void* user_data); /* Since 1.0.29 */
void* user_data) /* Since 1.0.29 */
G_GNUC_WARN_UNUSED_RESULT;
GBinderServiceManager*
gbinder_servicemanager_ref(
@@ -125,7 +129,9 @@ gbinder_servicemanager_list(
char**
gbinder_servicemanager_list_sync(
GBinderServiceManager* sm);
GBinderServiceManager* sm)
G_GNUC_WARN_UNUSED_RESULT
G_GNUC_MALLOC;
gulong
gbinder_servicemanager_get_service(

View File

@@ -1,5 +1,5 @@
Name: libgbinder
Version: 1.0.43
Version: 1.1.3
Release: 0
Summary: Binder client library
License: BSD
@@ -30,10 +30,14 @@ This package contains the development library for %{name}.
%build
make LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
make -C test/binder-list release
make -C test/binder-ping release
%install
rm -rf %{buildroot}
make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev
make -C test/binder-list DESTDIR=%{buildroot} install
make -C test/binder-ping DESTDIR=%{buildroot} install
%check
make -C unit test
@@ -51,3 +55,16 @@ make -C unit test
%{_libdir}/pkgconfig/*.pc
%{_libdir}/%{name}.so
%{_includedir}/gbinder/*.h
# Tools
%package tools
Summary: Binder tools
%description tools
Binder command line utilities
%files tools
%defattr(-,root,root,-)
%{_bindir}/binder-list
%{_bindir}/binder-ping

384
src/gbinder_config.c Normal file
View File

@@ -0,0 +1,384 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "gbinder_config.h"
#include "gbinder_eventloop_p.h"
#include "gbinder_log.h"
#include <gutil_strv.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/*
* 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 }
};
/* Presets sorted by API level in descending order */
static const GBinderConfigPreset gbinder_config_presets[] = {
{ 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:
*/

78
src/gbinder_config.h Normal file
View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#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:
*/

View File

@@ -447,8 +447,14 @@ gbinder_driver_handle_transaction(
tx.code, tx.flags, &status);
break;
case GBINDER_LOCAL_TRANSACTION_SUPPORTED:
reply = gbinder_handler_transact(h, obj, req, tx.code, tx.flags,
&status);
/*
* NULL GBinderHandler means that this is a synchronous call
* executed on the main thread, meaning that we can call the
* local object directly.
*/
reply = h ? gbinder_handler_transact(h, obj, req, tx.code, tx.flags,
&status) : gbinder_local_object_handle_transaction(obj, req,
tx.code, tx.flags, &status);
break;
default:
GWARN("Unhandled transaction %s 0x%08x", iface, tx.code);
@@ -707,7 +713,7 @@ gbinder_driver_new(
max_threads, strerror(errno));
}
/* Choose the protocol based on the device name
* if none is explicitely specified */
* if none is explicitly specified */
self->protocol = protocol ? protocol :
gbinder_rpc_protocol_for_device(dev);
return self;
@@ -947,7 +953,7 @@ gbinder_driver_read(
if (ret >= 0) {
/* Loop until we have handled all the incoming commands */
gbinder_driver_handle_commands(self, reg, handler, &rb);
while (rb.buf.consumed) {
while (rb.buf.consumed && gbinder_handler_can_loop(handler)) {
ret = gbinder_driver_write_read(self, NULL, &rb.buf);
if (ret >= 0) {
gbinder_driver_handle_commands(self, reg, handler, &rb);
@@ -963,6 +969,7 @@ int
gbinder_driver_transact(
GBinderDriver* self,
GBinderObjectRegistry* reg,
GBinderHandler* handler,
guint32 handle,
guint32 code,
GBinderLocalRequest* req,
@@ -1019,7 +1026,7 @@ gbinder_driver_transact(
if (err < 0) {
txstatus = err;
} else {
txstatus = gbinder_driver_txstatus(self, reg, NULL, &rb, reply);
txstatus = gbinder_driver_txstatus(self, reg, handler, &rb, reply);
}
}
@@ -1028,14 +1035,14 @@ gbinder_driver_transact(
GASSERT(write.consumed == write.size || txstatus > 0);
/* Loop until we have handled all the incoming commands */
gbinder_driver_handle_commands(self, reg, NULL, &rb);
gbinder_driver_handle_commands(self, reg, handler, &rb);
while (rb.buf.consumed) {
int err = gbinder_driver_write_read(self, NULL, &rb.buf);
if (err < 0) {
txstatus = err;
break;
} else {
gbinder_driver_handle_commands(self, reg, NULL, &rb);
gbinder_driver_handle_commands(self, reg, handler, &rb);
}
}
}
@@ -1058,7 +1065,7 @@ gbinder_driver_ping(
gbinder_local_request_init_writer(req, &writer);
protocol->write_ping(&writer);
ret = gbinder_driver_transact(self, reg, handle, protocol->ping_tx,
ret = gbinder_driver_transact(self, reg, NULL, handle, protocol->ping_tx,
req, reply);
gbinder_local_request_unref(req);

View File

@@ -144,6 +144,7 @@ int
gbinder_driver_transact(
GBinderDriver* driver,
GBinderObjectRegistry* reg,
GBinderHandler* handler,
guint32 handle,
guint32 code,
GBinderLocalRequest* request,

View File

@@ -36,6 +36,7 @@
#include "gbinder_types_p.h"
typedef struct gbinder_handler_functions {
gboolean (*can_loop)(GBinderHandler* handler);
GBinderLocalReply* (*transact)(GBinderHandler* handler,
GBinderLocalObject* obj, GBinderRemoteRequest* req, guint code,
guint flags, int* status);
@@ -47,6 +48,14 @@ struct gbinder_handler {
/* Inline wrappers */
GBINDER_INLINE_FUNC
gboolean
gbinder_handler_can_loop(
GBinderHandler* self)
{
return self && self->f->can_loop && self->f->can_loop(self);
}
GBINDER_INLINE_FUNC
GBinderLocalReply*
gbinder_handler_transact(

View File

@@ -36,6 +36,7 @@
#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"
@@ -162,6 +163,11 @@ struct gbinder_ipc_looper {
int txfd[2];
};
typedef struct gbinder_ipc_tx_handler {
int pipefd[2];
int txfd[2];
} GBinderIpcTxHandler;
typedef struct gbinder_ipc_tx_priv GBinderIpcTxPriv;
typedef
@@ -204,6 +210,40 @@ GBinderIpcLooper*
gbinder_ipc_looper_new(
GBinderIpc* ipc);
/*==========================================================================*
* 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;
}
/*==========================================================================*
* GBinderIpcLooperTx
*==========================================================================*/
@@ -566,6 +606,16 @@ gbinder_ipc_looper_count_primary(
return n;
}
static
gboolean
gbinder_ipc_looper_can_loop(
GBinderHandler* handler)
{
GBinderIpcLooper* looper = G_CAST(handler,GBinderIpcLooper,handler);
return !g_atomic_int_get(&looper->exit);
}
static
GBinderLocalReply*
gbinder_ipc_looper_transact(
@@ -589,7 +639,6 @@ gbinder_ipc_looper_transact(
GBinderIpcLooperTx* tx = gbinder_ipc_looper_tx_new(obj, code, flags,
req, looper->txfd);
GBinderIpcPriv* priv = ipc->priv;
struct pollfd fds[2];
guint8 done = 0;
gboolean was_blocked = FALSE;
/* Let GBinderLocalObject handle the transaction on the main thread */
@@ -598,69 +647,52 @@ gbinder_ipc_looper_transact(
gbinder_ipc_looper_tx_ref(tx), gbinder_ipc_looper_tx_done);
/* Wait for either transaction completion or looper shutdown */
memset(fds, 0, sizeof(fds));
fds[0].fd = looper->pipefd[0];
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
fds[1].fd = tx->pipefd[0];
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
poll(fds, 2, -1);
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;
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
/* Normal completion */
if (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)) {
GDEBUG("Primary looper %s is blocked", looper->name);
looper->next = priv->blocked_loopers;
priv->blocked_loopers = looper;
was_blocked = TRUE;
/* Lock */
g_mutex_lock(&priv->looper_mutex);
if (gbinder_ipc_looper_remove_primary(looper)) {
GDEBUG("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;
}
/* 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 */
}
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);
}
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;
memset(fds, 0, sizeof(fds));
fds[0].fd = looper->pipefd[0];
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
fds[1].fd = tx->pipefd[0];
fds[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
poll(fds, 2, -1);
if ((fds[1].revents & POLLIN) &&
read(fds[1].fd, &done, sizeof(done)) == 1) {
GDEBUG("Looper %s is released", looper->name);
GASSERT(done == TX_DONE);
}
/* Block until asynchronous transaction gets completed. */
done = 0;
if (gbinder_ipc_wait(looper->pipefd[0], tx->pipefd[0], &done)) {
GDEBUG("Looper %s is released", looper->name);
GASSERT(done == TX_DONE);
}
}
@@ -668,17 +700,16 @@ gbinder_ipc_looper_transact(
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.
*/
looper->txfd[0] = looper->txfd[1] = -1;
}
} else {
gbinder_ipc_looper_tx_unref(tx, FALSE);
}
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);
@@ -803,6 +834,7 @@ gbinder_ipc_looper_new(
/* Note: this call can actually fail */
if (!pipe(fd)) {
static const GBinderHandlerFunctions handler_functions = {
.can_loop = gbinder_ipc_looper_can_loop,
.transact = gbinder_ipc_looper_transact
};
GError* error = NULL;
@@ -875,13 +907,15 @@ gbinder_ipc_looper_stop(
GBinderIpcLooper* looper)
{
/* Caller checks looper for NULL */
if (looper->thread && looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
if (looper->thread) {
GDEBUG("Stopping looper %s", looper->name);
g_atomic_int_set(&looper->exit, TRUE);
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
if (looper->thread != g_thread_self()) {
guint8 done = TX_DONE;
if (write(looper->pipefd[1], &done, sizeof(done)) <= 0) {
looper->thread = NULL;
}
}
}
}
@@ -917,6 +951,117 @@ gbinder_ipc_looper_join(
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
*==========================================================================*/
@@ -1288,19 +1433,24 @@ void
gbinder_ipc_tx_internal_exec(
GBinderIpcTxPriv* priv)
{
static const GBinderHandlerFunctions handler_fn = {
.can_loop = NULL,
.transact = gbinder_ipc_tx_handler_transact
};
GBinderIpcTxInternal* tx = gbinder_ipc_tx_internal_cast(priv);
GBinderIpcTx* pub = &priv->pub;
GBinderIpc* self = pub->ipc;
GBinderObjectRegistry* reg = &self->priv->object_registry;
GBinderHandler handler = { &handler_fn };
/* Perform synchronous transaction */
if (tx->flags & GBINDER_TX_FLAG_ONEWAY) {
tx->status = gbinder_driver_transact(self->driver, reg, tx->handle,
tx->code, tx->req, NULL);
tx->status = gbinder_driver_transact(self->driver, reg, &handler,
tx->handle, tx->code, tx->req, NULL);
} else {
tx->reply = gbinder_remote_reply_new(&self->priv->object_registry);
tx->status = gbinder_driver_transact(self->driver, reg, tx->handle,
tx->code, tx->req, tx->reply);
tx->status = gbinder_driver_transact(self->driver, reg, &handler,
tx->handle, tx->code, tx->req, tx->reply);
if (tx->status != GBINDER_STATUS_OK &&
gbinder_remote_reply_is_empty(tx->reply)) {
/* Drop useless reply */
@@ -1435,12 +1585,14 @@ gbinder_ipc_tx_proc(
GBinderIpc*
gbinder_ipc_new(
const char* dev,
const GBinderRpcProtocol* protocol)
const char* dev)
{
GBinderIpc* self = NULL;
const GBinderRpcProtocol* protocol;
if (!dev || !dev[0]) dev = GBINDER_DEFAULT_BINDER;
protocol = gbinder_rpc_protocol_for_device(dev); /* Never returns NULL */
/* Lock */
pthread_mutex_lock(&gbinder_ipc_mutex);
if (gbinder_ipc_table) {
@@ -1512,8 +1664,8 @@ gbinder_ipc_transact_sync_reply(
GBinderIpcPriv* priv = self->priv;
GBinderObjectRegistry* reg = &priv->object_registry;
GBinderRemoteReply* reply = gbinder_remote_reply_new(reg);
int ret = gbinder_driver_transact(self->driver, reg, handle, code,
req, reply);
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)) {
@@ -1538,7 +1690,7 @@ gbinder_ipc_transact_sync_oneway(
GBinderIpcPriv* priv = self->priv;
return gbinder_driver_transact(self->driver, &priv->object_registry,
handle, code, req, NULL);
NULL, handle, code, req, NULL);
} else {
return (-EINVAL);
}
@@ -1611,6 +1763,14 @@ gbinder_ipc_cancel(
}
}
gboolean
gbinder_ipc_set_max_threads(
GBinderIpc* self,
gint max)
{
return g_thread_pool_set_max_threads(self->priv->tx_pool, max, NULL);
}
/*==========================================================================*
* Internals
*==========================================================================*/

View File

@@ -69,8 +69,7 @@ void
GBinderIpc*
gbinder_ipc_new(
const char* dev,
const GBinderRpcProtocol* protocol)
const char* dev)
GBINDER_INTERNAL;
GBinderIpc*
@@ -170,12 +169,19 @@ gbinder_ipc_remote_object_disposed(
GBinderRemoteObject* obj)
GBINDER_INTERNAL;
/* Needed by unit tests */
gboolean
gbinder_ipc_set_max_threads(
GBinderIpc* self,
gint max_threads)
GBINDER_INTERNAL;
/* Declared for unit tests */
__attribute__((destructor))
void
gbinder_ipc_exit(
void)
GBINDER_INTERNAL;
GBINDER_INTERNAL
GBINDER_DESTRUCTOR;
#endif /* GBINDER_IPC_H */

View File

@@ -339,38 +339,50 @@ gbinder_local_object_new(
if (G_LIKELY(ipc)) {
GBinderLocalObject* self = g_object_new
(GBINDER_TYPE_LOCAL_OBJECT, NULL);
GBinderLocalObjectPriv* priv = self->priv;
guint i = 0, n = gutil_strv_length((char**)ifaces);
gboolean append_base_interface;
if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
append_base_interface = TRUE;
n++;
} else {
append_base_interface = FALSE;
}
priv->ifaces = g_new(char*, n + 1);
if (ifaces) {
while (*ifaces) {
priv->ifaces[i++] = g_strdup(*ifaces++);
}
}
if (append_base_interface) {
priv->ifaces[i++] = g_strdup(hidl_base_interface);
}
priv->ifaces[i] = NULL;
self->ipc = gbinder_ipc_ref(ipc);
self->ifaces = (const char**)priv->ifaces;
priv->txproc = txproc;
priv->user_data = user_data;
gbinder_local_object_init_base(self, ipc, ifaces, txproc, user_data);
gbinder_ipc_register_local_object(ipc, self);
return self;
}
return NULL;
}
void
gbinder_local_object_init_base(
GBinderLocalObject* self,
GBinderIpc* ipc,
const char* const* ifaces,
GBinderLocalTransactFunc txproc,
void* user_data)
{
GBinderLocalObjectPriv* priv = self->priv;
guint i = 0, n = gutil_strv_length((char**)ifaces);
gboolean append_base_interface;
if (g_strcmp0(gutil_strv_last((char**)ifaces), hidl_base_interface)) {
append_base_interface = TRUE;
n++;
} else {
append_base_interface = FALSE;
}
priv->ifaces = g_new(char*, n + 1);
if (ifaces) {
while (*ifaces) {
priv->ifaces[i++] = g_strdup(*ifaces++);
}
}
if (append_base_interface) {
priv->ifaces[i++] = g_strdup(hidl_base_interface);
}
priv->ifaces[i] = NULL;
self->ipc = gbinder_ipc_ref(ipc);
self->ifaces = (const char**)priv->ifaces;
priv->txproc = txproc;
priv->user_data = user_data;
}
GBinderLocalObject*
gbinder_local_object_ref(
GBinderLocalObject* self)

View File

@@ -83,10 +83,21 @@ GType gbinder_local_object_get_type(void) GBINDER_INTERNAL;
#define GBINDER_TYPE_LOCAL_OBJECT (gbinder_local_object_get_type())
#define GBINDER_LOCAL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObject))
#define GBINDER_LOCAL_OBJECT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), \
GBINDER_TYPE_LOCAL_OBJECT, GBinderLocalObjectClass)
#define gbinder_local_object_dev(obj) (gbinder_driver_dev((obj)->ipc->driver))
#define gbinder_local_object_io(obj) (gbinder_driver_io((obj)->ipc->driver))
void
gbinder_local_object_init_base(
GBinderLocalObject* self,
GBinderIpc* ipc,
const char* const* ifaces,
GBinderLocalTransactFunc txproc,
void* user_data)
GBINDER_INTERNAL;
gulong
gbinder_local_object_add_weak_refs_changed_handler(
GBinderLocalObject* obj,

View File

@@ -115,6 +115,7 @@ gbinder_remote_object_reanimate(
if (gbinder_driver_ping(ipc->driver, reg, self->handle) == 0) {
/* Wow, it's alive! */
self->dead = FALSE;
gbinder_ipc_looper_check(self->ipc); /* For death notifications */
gbinder_driver_acquire(ipc->driver, self->handle);
gbinder_driver_request_death_notification(ipc->driver, self);
}
@@ -151,6 +152,7 @@ gbinder_remote_object_new(
self->ipc = gbinder_ipc_ref(ipc);
self->handle = handle;
if (!(self->dead = dead)) {
gbinder_ipc_looper_check(self->ipc); /* For death notifications */
gbinder_driver_acquire(ipc->driver, handle);
gbinder_driver_request_death_notification(ipc->driver, self);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -33,6 +33,12 @@
#include "gbinder_rpc_protocol.h"
#include "gbinder_reader.h"
#include "gbinder_writer.h"
#include "gbinder_config.h"
#include "gbinder_log.h"
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
#define UNSET_WORK_SOURCE (-1)
/*==========================================================================*
* GBinderIpcProtocol callbacks (see Parcel::writeInterfaceToken in Android)
@@ -40,19 +46,39 @@
*
* 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;
/*==========================================================================*
* /dev/binder
* The original AIDL protocol.
*==========================================================================*/
/* No idea what that is... */
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
#define BINDER_RPC_FLAGS (STRICT_MODE_PENALTY_GATHER)
static
void
gbinder_rpc_protocol_binder_write_ping(
gbinder_rpc_protocol_aidl_write_ping(
GBinderWriter* writer)
{
/* No payload */
@@ -60,7 +86,7 @@ gbinder_rpc_protocol_binder_write_ping(
static
void
gbinder_rpc_protocol_binder_write_rpc_header(
gbinder_rpc_protocol_aidl_write_rpc_header(
GBinderWriter* writer,
const char* iface)
{
@@ -75,7 +101,7 @@ gbinder_rpc_protocol_binder_write_rpc_header(
static
const char*
gbinder_rpc_protocol_binder_read_rpc_header(
gbinder_rpc_protocol_aidl_read_rpc_header(
GBinderReader* reader,
guint32 txcode,
char** iface)
@@ -91,13 +117,69 @@ gbinder_rpc_protocol_binder_read_rpc_header(
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
};
/*==========================================================================*
* /dev/hwbinder
* AIDL protocol appeared in Android 10 (API level 29)
*==========================================================================*/
static
void
gbinder_rpc_protocol_hwbinder_write_rpc_header(
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
};
/*==========================================================================*
* The original /dev/hwbinder protocol.
*==========================================================================*/
static
void
gbinder_rpc_protocol_hidl_write_rpc_header(
GBinderWriter* writer,
const char* iface)
{
@@ -109,16 +191,16 @@ gbinder_rpc_protocol_hwbinder_write_rpc_header(
static
void
gbinder_rpc_protocol_hwbinder_write_ping(
gbinder_rpc_protocol_hidl_write_ping(
GBinderWriter* writer)
{
gbinder_rpc_protocol_hwbinder_write_rpc_header(writer,
gbinder_rpc_protocol_hidl_write_rpc_header(writer,
"android.hidl.base@1.0::IBase");
}
static
const char*
gbinder_rpc_protocol_hwbinder_read_rpc_header(
gbinder_rpc_protocol_hidl_read_rpc_header(
GBinderReader* reader,
guint32 txcode,
char** iface)
@@ -127,30 +209,126 @@ gbinder_rpc_protocol_hwbinder_read_rpc_header(
return gbinder_reader_read_string8(reader);
}
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
};
/*==========================================================================*
* Implementation
*==========================================================================*/
/* All known protocols */
static const GBinderRpcProtocol* gbinder_rpc_protocol_list[] = {
&gbinder_rpc_protocol_aidl,
&gbinder_rpc_protocol_aidl2,
&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_binder = {
.ping_tx = GBINDER_PING_TRANSACTION,
.write_ping = gbinder_rpc_protocol_binder_write_ping,
.write_rpc_header = gbinder_rpc_protocol_binder_write_rpc_header,
.read_rpc_header = gbinder_rpc_protocol_binder_read_rpc_header
};
const GBinderRpcProtocol gbinder_rpc_protocol_hwbinder = {
.ping_tx = HIDL_PING_TRANSACTION,
.write_ping = gbinder_rpc_protocol_hwbinder_write_ping,
.write_rpc_header = gbinder_rpc_protocol_hwbinder_write_rpc_header,
.read_rpc_header = gbinder_rpc_protocol_hwbinder_read_rpc_header
};
const GBinderRpcProtocol*
gbinder_rpc_protocol_for_device(
const char* dev)
{
return (dev && !strcmp(dev, GBINDER_DEFAULT_HWBINDER)) ?
&gbinder_rpc_protocol_hwbinder : &gbinder_rpc_protocol_binder;
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;
}
/*

View File

@@ -13,7 +13,6 @@
* 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.
* 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.
@@ -37,11 +36,12 @@
#include "gbinder_types_p.h"
/*
* For whatever reason services communicating via /dev/binder
* and /dev/hwbinder use slightly different RPC headers.
* 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);
@@ -49,15 +49,19 @@ struct gbinder_rpc_protocol {
char** iface);
};
extern const GBinderRpcProtocol gbinder_rpc_protocol_binder GBINDER_INTERNAL;
extern const GBinderRpcProtocol gbinder_rpc_protocol_hwbinder 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 */
/*

View File

@@ -34,6 +34,7 @@
#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"
@@ -47,6 +48,51 @@
#include <errno.h>
/*==========================================================================*
*
* 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 },
{ "hidl", gbinder_servicemanager_hidl_get_type }
};
#define SERVICEMANAGER_TYPE_AIDL (gbinder_servicemanager_types + 0)
#define SERVICEMANAGER_TYPE_HIDL (gbinder_servicemanager_types + 2)
#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)
@@ -75,9 +121,6 @@ G_DEFINE_ABSTRACT_TYPE(GBinderServiceManager, gbinder_servicemanager,
#define GBINDER_SERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER, \
GBinderServiceManager)
#define GBINDER_SERVICEMANAGER_CLASS(klass) \
G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER, \
GBinderServiceManagerClass)
#define GBINDER_SERVICEMANAGER_GET_CLASS(obj) \
G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER, \
GBinderServiceManagerClass)
@@ -408,6 +451,64 @@ gbinder_servicemanager_autorelease_cb(
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
*==========================================================================*/
@@ -424,9 +525,9 @@ gbinder_servicemanager_new_with_type(
GBinderIpc* ipc;
if (!dev) dev = klass->default_device;
ipc = gbinder_ipc_new(dev, klass->rpc_protocol);
ipc = gbinder_ipc_new(dev);
if (ipc) {
/* Create a possible dead remote object */
/* Create a possibly dead remote object */
GBinderRemoteObject* object = gbinder_ipc_get_remote_object
(ipc, GBINDER_SERVICEMANAGER_HANDLE, TRUE);
@@ -517,11 +618,35 @@ GBinderServiceManager*
gbinder_servicemanager_new(
const char* dev)
{
if (!g_strcmp0(dev, GBINDER_DEFAULT_HWBINDER)) {
return gbinder_hwservicemanager_new(dev);
} else {
return gbinder_defaultservicemanager_new(dev);
if (dev) {
const GBinderServiceManagerType* type = NULL;
if (!gbinder_servicemanager_map) {
const GBinderServiceManagerType* t;
/* One-time initialization */
gbinder_servicemanager_map = gbinder_servicemanager_load_config();
/* "Default" is a special value stored in a special variable */
t = g_hash_table_lookup(gbinder_servicemanager_map, CONF_DEFAULT);
if (t) {
g_hash_table_remove(gbinder_servicemanager_map, CONF_DEFAULT);
gbinder_servicemanager_default = t;
} else {
gbinder_servicemanager_default = SERVICEMANAGER_TYPE_DEFAULT;
}
}
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);
}
return NULL;
}
GBinderLocalObject*
@@ -870,6 +995,28 @@ gbinder_servicemanager_remove_handlers(
}
}
/*
* 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);
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type
(gbinder_servicemanager_hidl_get_type(), dev);
}
/*==========================================================================*
* Internals
*==========================================================================*/

View File

@@ -30,8 +30,9 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "gbinder_servicemanager_aidl.h"
#include "gbinder_servicepoll.h"
#include "gbinder_eventloop_p.h"
#include "gbinder_log.h"
@@ -40,61 +41,47 @@
#include <gbinder_local_request.h>
#include <gbinder_remote_reply.h>
#include <gutil_macros.h>
#include <errno.h>
#include <pthread.h>
typedef struct gbinder_defaultservicemanager_watch {
typedef struct gbinder_servicemanager_aidl_watch {
GBinderServicePoll* poll;
char* name;
gulong handler_id;
GBinderEventLoopTimeout* notify;
} GBinderDefaultServiceManagerWatch;
} GBinderServiceManagerAidlWatch;
typedef GBinderServiceManagerClass GBinderDefaultServiceManagerClass;
typedef struct gbinder_defaultservicemanager {
GBinderServiceManager manager;
struct gbinder_servicemanager_aidl_priv {
GBinderServicePoll* poll;
GHashTable* watch_table;
} GBinderDefaultServiceManager;
};
G_DEFINE_TYPE(GBinderDefaultServiceManager,
gbinder_defaultservicemanager,
G_DEFINE_TYPE(GBinderServiceManagerAidl,
gbinder_servicemanager_aidl,
GBINDER_TYPE_SERVICEMANAGER)
#define PARENT_CLASS gbinder_defaultservicemanager_parent_class
#define GBINDER_TYPE_DEFAULTSERVICEMANAGER \
gbinder_defaultservicemanager_get_type()
#define GBINDER_DEFAULTSERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_DEFAULTSERVICEMANAGER, \
GBinderDefaultServiceManager)
#define PARENT_CLASS gbinder_servicemanager_aidl_parent_class
#define GBINDER_SERVICEMANAGER_AIDL(obj) \
G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
GBinderServiceManagerAidl)
#define GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(obj) \
G_TYPE_INSTANCE_GET_CLASS((obj), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
GBinderServiceManagerAidlClass)
enum gbinder_defaultservicemanager_calls {
enum gbinder_servicemanager_aidl_calls {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION
};
#define DEFAULTSERVICEMANAGER_IFACE "android.os.IServiceManager"
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type
(GBINDER_TYPE_DEFAULTSERVICEMANAGER, dev);
}
#define SERVICEMANAGER_AIDL_IFACE "android.os.IServiceManager"
static
void
gbinder_defaultservicemanager_watch_proc(
gbinder_servicemanager_aidl_watch_proc(
GBinderServicePoll* poll,
const char* name_added,
void* user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
GBinderServiceManagerAidlWatch* watch = user_data;
if (!g_strcmp0(name_added, watch->name)) {
GBinderServiceManager* manager =
@@ -110,10 +97,10 @@ gbinder_defaultservicemanager_watch_proc(
static
gboolean
gbinder_defaultservicemanager_watch_notify(
gbinder_servicemanager_aidl_watch_notify(
gpointer user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
GBinderServiceManagerAidlWatch* watch = user_data;
GBinderServiceManager* manager = gbinder_servicepoll_manager(watch->poll);
char* name = g_strdup(watch->name);
@@ -126,54 +113,75 @@ gbinder_defaultservicemanager_watch_notify(
static
void
gbinder_defaultservicemanager_watch_free(
gbinder_servicemanager_aidl_watch_free(
gpointer user_data)
{
GBinderDefaultServiceManagerWatch* watch = user_data;
GBinderServiceManagerAidlWatch* 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(GBinderDefaultServiceManagerWatch, watch);
g_slice_free(GBinderServiceManagerAidlWatch, watch);
}
static
GBinderDefaultServiceManagerWatch*
gbinder_defaultservicemanager_watch_new(
GBinderDefaultServiceManager* manager,
GBinderServiceManagerAidlWatch*
gbinder_servicemanager_aidl_watch_new(
GBinderServiceManagerAidl* self,
const char* name)
{
GBinderDefaultServiceManagerWatch* watch =
g_slice_new0(GBinderDefaultServiceManagerWatch);
GBinderServiceManagerAidlPriv* priv = self->priv;
GBinderServiceManagerAidlWatch* watch =
g_slice_new0(GBinderServiceManagerAidlWatch);
watch->name = g_strdup(name);
watch->poll = gbinder_servicepoll_new(&manager->manager, &manager->poll);
watch->handler_id = gbinder_servicepoll_add_handler(watch->poll,
gbinder_defaultservicemanager_watch_proc, watch);
watch->poll = gbinder_servicepoll_new(&self->manager, &priv->poll);
watch->handler_id = gbinder_servicepoll_add_handler(priv->poll,
gbinder_servicemanager_aidl_watch_proc, watch);
return watch;
}
static
GBinderLocalRequest*
gbinder_servicemanager_list_services_req(
GBinderServiceManager* self,
gbinder_servicemanager_aidl_list_services_req(
GBinderClient* client,
gint32 index)
{
return gbinder_local_request_append_int32
(gbinder_client_new_request(self->client), 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_defaultservicemanager_list(
GBinderServiceManager* self)
gbinder_servicemanager_aidl_list(
GBinderServiceManager* manager)
{
GPtrArray* list = g_ptr_array_new();
GBinderLocalRequest* req = gbinder_servicemanager_list_services_req(self,0);
GBinderClient* client = manager->client;
GBinderServiceManagerAidlClass* klass =
GBINDER_SERVICEMANAGER_AIDL_GET_CLASS(manager);
GBinderLocalRequest* req = klass->list_services_req(client, 0);
GBinderRemoteReply* reply;
while ((reply = gbinder_client_transact_sync_reply(self->client,
while ((reply = gbinder_client_transact_sync_reply(client,
LIST_SERVICES_TRANSACTION, req, NULL)) != NULL) {
char* service = gbinder_remote_reply_read_string16(reply);
@@ -181,7 +189,7 @@ gbinder_defaultservicemanager_list(
if (service) {
g_ptr_array_add(list, service);
gbinder_local_request_unref(req);
req = gbinder_servicemanager_list_services_req(self, list->len);
req = klass->list_services_req(client, list->len);
} else {
break;
}
@@ -194,7 +202,7 @@ gbinder_defaultservicemanager_list(
static
GBinderRemoteObject*
gbinder_defaultservicemanager_get_service(
gbinder_servicemanager_aidl_get_service(
GBinderServiceManager* self,
const char* name,
int* status)
@@ -215,20 +223,16 @@ gbinder_defaultservicemanager_get_service(
static
int
gbinder_defaultservicemanager_add_service(
GBinderServiceManager* self,
gbinder_servicemanager_aidl_add_service(
GBinderServiceManager* manager,
const char* name,
GBinderLocalObject* obj)
{
int status;
GBinderRemoteReply* reply;
GBinderLocalRequest* req = gbinder_client_new_request(self->client);
gbinder_local_request_append_string16(req, name);
gbinder_local_request_append_local_object(req, obj);
gbinder_local_request_append_int32(req, 0);
reply = gbinder_client_transact_sync_reply(self->client,
GBinderClient* client = manager->client;
GBinderLocalRequest* req = GBINDER_SERVICEMANAGER_AIDL_GET_CLASS
(manager)->add_service_req(client, name, obj);
GBinderRemoteReply* reply = gbinder_client_transact_sync_reply(client,
ADD_SERVICE_TRANSACTION, req, &status);
gbinder_remote_reply_unref(reply);
@@ -238,7 +242,7 @@ gbinder_defaultservicemanager_add_service(
static
GBINDER_SERVICEMANAGER_NAME_CHECK
gbinder_defaultservicemanager_check_name(
gbinder_servicemanager_aidl_check_name(
GBinderServiceManager* self,
const char* name)
{
@@ -247,69 +251,84 @@ gbinder_defaultservicemanager_check_name(
static
gboolean
gbinder_defaultservicemanager_watch(
gbinder_servicemanager_aidl_watch(
GBinderServiceManager* manager,
const char* name)
{
GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(manager);
GBinderDefaultServiceManagerWatch* watch =
gbinder_defaultservicemanager_watch_new(self, name);
GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(manager);
GBinderServiceManagerAidlPriv* priv = self->priv;
GBinderServiceManagerAidlWatch* watch =
gbinder_servicemanager_aidl_watch_new(self, name);
g_hash_table_replace(self->watch_table, watch->name, watch);
g_hash_table_replace(priv->watch_table, watch->name, watch);
if (gbinder_servicepoll_is_known_name(watch->poll, name)) {
watch->notify = gbinder_idle_add
(gbinder_defaultservicemanager_watch_notify, watch);
(gbinder_servicemanager_aidl_watch_notify, watch);
}
return TRUE;
}
static
void
gbinder_defaultservicemanager_unwatch(
gbinder_servicemanager_aidl_unwatch(
GBinderServiceManager* manager,
const char* name)
{
g_hash_table_remove(GBINDER_DEFAULTSERVICEMANAGER(manager)->watch_table,
name);
GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(manager);
GBinderServiceManagerAidlPriv* priv = self->priv;
g_hash_table_remove(priv->watch_table, name);
}
static
void
gbinder_defaultservicemanager_init(
GBinderDefaultServiceManager* self)
gbinder_servicemanager_aidl_init(
GBinderServiceManagerAidl* self)
{
self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, gbinder_defaultservicemanager_watch_free);
GBinderServiceManagerAidlPriv* priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
GBINDER_TYPE_SERVICEMANAGER_AIDL, GBinderServiceManagerAidlPriv);
self->priv = priv;
priv->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, gbinder_servicemanager_aidl_watch_free);
}
static
void
gbinder_defaultservicemanager_finalize(
gbinder_servicemanager_aidl_finalize(
GObject* object)
{
GBinderDefaultServiceManager* self = GBINDER_DEFAULTSERVICEMANAGER(object);
GBinderServiceManagerAidl* self = GBINDER_SERVICEMANAGER_AIDL(object);
GBinderServiceManagerAidlPriv* priv = self->priv;
g_hash_table_destroy(self->watch_table);
g_hash_table_destroy(priv->watch_table);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
}
static
void
gbinder_defaultservicemanager_class_init(
GBinderDefaultServiceManagerClass* klass)
gbinder_servicemanager_aidl_class_init(
GBinderServiceManagerAidlClass* klass)
{
klass->iface = DEFAULTSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_BINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
GBinderServiceManagerClass* manager = GBINDER_SERVICEMANAGER_CLASS(klass);
GObjectClass* object = G_OBJECT_CLASS(klass);
klass->list = gbinder_defaultservicemanager_list;
klass->get_service = gbinder_defaultservicemanager_get_service;
klass->add_service = gbinder_defaultservicemanager_add_service;
klass->check_name = gbinder_defaultservicemanager_check_name;
g_type_class_add_private(klass, sizeof(GBinderServiceManagerAidlPriv));
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 */
klass->watch = gbinder_defaultservicemanager_watch;
klass->unwatch = gbinder_defaultservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = gbinder_defaultservicemanager_finalize;
manager->watch = gbinder_servicemanager_aidl_watch;
manager->unwatch = gbinder_servicemanager_aidl_unwatch;
object->finalize = gbinder_servicemanager_aidl_finalize;
}
/*

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#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;
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_CLASS(klass) \
G_TYPE_CHECK_CLASS_CAST((klass), GBINDER_TYPE_SERVICEMANAGER_AIDL, \
GBinderServiceManagerAidlClass)
#endif /* GBINDER_SERVICEMANAGER_AIDL_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "gbinder_servicemanager_aidl.h"
#include <gbinder_client.h>
#include <gbinder_local_request.h>
/* 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)
#define PARENT_CLASS gbinder_servicemanager_aidl2_parent_class
#define DUMP_FLAG_PRIORITY_DEFAULT (0x08)
#define DUMP_FLAG_PRIORITY_ALL (0x0f)
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* cls)
{
cls->list_services_req = gbinder_servicemanager_aidl2_list_services_req;
cls->add_service_req = gbinder_servicemanager_aidl2_add_service_req;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -31,7 +31,6 @@
*/
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_log.h"
#include <gbinder_client.h>
@@ -42,30 +41,30 @@
#include <gbinder_reader.h>
#include <errno.h>
#include <pthread.h>
typedef struct gbinder_hwservicemanager_watch {
typedef struct gbinder_servicemanager_hidl_watch {
char* name;
GBinderLocalObject* callback;
} GBinderHwServiceManagerWatch;
} GBinderServiceManagerHidlWatch;
typedef GBinderServiceManagerClass GBinderHwServiceManagerClass;
typedef struct gbinder_hwservicemanager {
typedef GBinderServiceManagerClass GBinderServiceManagerHidlClass;
typedef struct gbinder_servicemanager_hidl {
GBinderServiceManager manager;
GHashTable* watch_table;
} GBinderHwServiceManager;
} GBinderServiceManagerHidl;
G_DEFINE_TYPE(GBinderHwServiceManager,
gbinder_hwservicemanager,
G_DEFINE_TYPE(GBinderServiceManagerHidl,
gbinder_servicemanager_hidl,
GBINDER_TYPE_SERVICEMANAGER)
#define PARENT_CLASS gbinder_hwservicemanager_parent_class
#define GBINDER_TYPE_HWSERVICEMANAGER (gbinder_hwservicemanager_get_type())
#define GBINDER_HWSERVICEMANAGER(obj) \
G_TYPE_CHECK_INSTANCE_CAST((obj), GBINDER_TYPE_HWSERVICEMANAGER, \
GBinderHwServiceManager)
#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_hwservicemanager_calls {
enum gbinder_servicemanager_hidl_calls {
GET_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
ADD_TRANSACTION,
GET_TRANSPORT_TRANSACTION,
@@ -76,18 +75,18 @@ enum gbinder_hwservicemanager_calls {
REGISTER_PASSTHROUGH_CLIENT_TRANSACTION
};
enum gbinder_hwservicemanager_notifications {
enum gbinder_servicemanager_hidl_notifications {
ON_REGISTRATION_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION
};
#define HWSERVICEMANAGER_IFACE "android.hidl.manager@1.0::IServiceManager"
#define HWSERVICEMANAGER_NOTIFICATION_IFACE \
#define SERVICEMANAGER_HIDL_IFACE "android.hidl.manager@1.0::IServiceManager"
#define SERVICEMANAGER_HIDL_NOTIFICATION_IFACE \
"android.hidl.manager@1.0::IServiceNotification"
static
void
gbinder_hwservicemanager_handle_registration(
GBinderHwServiceManager* self,
gbinder_servicemanager_hidl_handle_registration(
GBinderServiceManagerHidl* self,
GBinderReader* reader)
{
char* fqname = gbinder_reader_read_hidl_string(reader);
@@ -111,7 +110,7 @@ gbinder_hwservicemanager_handle_registration(
static
GBinderLocalReply*
gbinder_hwservicemanager_notification(
gbinder_servicemanager_hidl_notification(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
@@ -119,22 +118,22 @@ gbinder_hwservicemanager_notification(
int* status,
void* user_data)
{
GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(user_data);
GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(user_data);
const char* iface = gbinder_remote_request_interface(req);
if (!g_strcmp0(iface, HWSERVICEMANAGER_NOTIFICATION_IFACE)) {
if (!g_strcmp0(iface, SERVICEMANAGER_HIDL_NOTIFICATION_IFACE)) {
GBinderReader reader;
gbinder_remote_request_init_reader(req, &reader);
switch (code) {
case ON_REGISTRATION_TRANSACTION:
GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u onRegistration",
GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u onRegistration",
code);
gbinder_hwservicemanager_handle_registration(self, &reader);
gbinder_servicemanager_hidl_handle_registration(self, &reader);
*status = GBINDER_STATUS_OK;
break;
default:
GDEBUG(HWSERVICEMANAGER_NOTIFICATION_IFACE " %u", code);
GDEBUG(SERVICEMANAGER_HIDL_NOTIFICATION_IFACE " %u", code);
*status = GBINDER_STATUS_FAILED;
break;
}
@@ -145,17 +144,9 @@ gbinder_hwservicemanager_notification(
return NULL;
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
{
return gbinder_servicemanager_new_with_type
(gbinder_hwservicemanager_get_type(), dev);
}
static
char**
gbinder_hwservicemanager_list(
gbinder_servicemanager_hidl_list(
GBinderServiceManager* self)
{
GBinderLocalRequest* req = gbinder_client_new_request(self->client);
@@ -184,7 +175,7 @@ gbinder_hwservicemanager_list(
static
GBinderRemoteObject*
gbinder_hwservicemanager_get_service(
gbinder_servicemanager_hidl_get_service(
GBinderServiceManager* self,
const char* fqinstance,
int* status)
@@ -231,7 +222,7 @@ gbinder_hwservicemanager_get_service(
static
int
gbinder_hwservicemanager_add_service(
gbinder_servicemanager_hidl_add_service(
GBinderServiceManager* self,
const char* name,
GBinderLocalObject* obj)
@@ -254,10 +245,10 @@ gbinder_hwservicemanager_add_service(
static
void
gbinder_hwservicemanager_watch_free(
gbinder_servicemanager_hidl_watch_free(
gpointer data)
{
GBinderHwServiceManagerWatch* watch = data;
GBinderServiceManagerHidlWatch* watch = data;
g_free(watch->name);
gbinder_local_object_drop(watch->callback);
@@ -266,7 +257,7 @@ gbinder_hwservicemanager_watch_free(
static
GBINDER_SERVICEMANAGER_NAME_CHECK
gbinder_hwservicemanager_check_name(
gbinder_servicemanager_hidl_check_name(
GBinderServiceManager* self,
const char* name)
{
@@ -287,32 +278,32 @@ gbinder_hwservicemanager_check_name(
static
char*
gbinder_hwservicemanager_normalize_name(
gbinder_servicemanager_hidl_normalize_name(
GBinderServiceManager* self,
const char* name)
{
/* Slash must be there, see gbinder_hwservicemanager_check_name() above */
/* Slash must be there, see gbinder_servicemanager_hidl_check_name() */
return g_strndup(name, strchr(name, '/') - name);
}
static
gboolean
gbinder_hwservicemanager_watch(
gbinder_servicemanager_hidl_watch(
GBinderServiceManager* manager,
const char* name)
{
GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(manager);
GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(manager);
GBinderLocalRequest* req = gbinder_client_new_request(manager->client);
GBinderRemoteReply* reply;
GBinderHwServiceManagerWatch* watch =
g_new0(GBinderHwServiceManagerWatch, 1);
GBinderServiceManagerHidlWatch* watch =
g_new0(GBinderServiceManagerHidlWatch, 1);
gboolean success = FALSE;
int status;
watch->name = g_strdup(name);
watch->callback = gbinder_servicemanager_new_local_object(manager,
HWSERVICEMANAGER_NOTIFICATION_IFACE,
gbinder_hwservicemanager_notification, self);
SERVICEMANAGER_HIDL_NOTIFICATION_IFACE,
gbinder_servicemanager_hidl_notification, self);
g_hash_table_replace(self->watch_table, watch->name, watch);
/* registerForNotifications(string fqName, string name,
@@ -344,28 +335,29 @@ gbinder_hwservicemanager_watch(
static
void
gbinder_hwservicemanager_unwatch(
gbinder_servicemanager_hidl_unwatch(
GBinderServiceManager* manager,
const char* name)
{
g_hash_table_remove(GBINDER_HWSERVICEMANAGER(manager)->watch_table, name);
g_hash_table_remove(GBINDER_SERVICEMANAGER_HIDL(manager)->
watch_table, name);
}
static
void
gbinder_hwservicemanager_init(
GBinderHwServiceManager* self)
gbinder_servicemanager_hidl_init(
GBinderServiceManagerHidl* self)
{
self->watch_table = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, gbinder_hwservicemanager_watch_free);
NULL, gbinder_servicemanager_hidl_watch_free);
}
static
void
gbinder_hwservicemanager_finalize(
gbinder_servicemanager_hidl_finalize(
GObject* object)
{
GBinderHwServiceManager* self = GBINDER_HWSERVICEMANAGER(object);
GBinderServiceManagerHidl* self = GBINDER_SERVICEMANAGER_HIDL(object);
g_hash_table_destroy(self->watch_table);
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
@@ -373,21 +365,20 @@ gbinder_hwservicemanager_finalize(
static
void
gbinder_hwservicemanager_class_init(
GBinderHwServiceManagerClass* klass)
gbinder_servicemanager_hidl_class_init(
GBinderServiceManagerHidlClass* klass)
{
klass->iface = HWSERVICEMANAGER_IFACE;
klass->iface = SERVICEMANAGER_HIDL_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
klass->list = gbinder_hwservicemanager_list;
klass->get_service = gbinder_hwservicemanager_get_service;
klass->add_service = gbinder_hwservicemanager_add_service;
klass->check_name = gbinder_hwservicemanager_check_name;
klass->normalize_name = gbinder_hwservicemanager_normalize_name;
klass->watch = gbinder_hwservicemanager_watch;
klass->unwatch = gbinder_hwservicemanager_unwatch;
G_OBJECT_CLASS(klass)->finalize = gbinder_hwservicemanager_finalize;
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;
}
/*

View File

@@ -64,7 +64,6 @@ typedef struct gbinder_servicemanager_class {
const char* iface;
const char* default_device;
const GBinderRpcProtocol* rpc_protocol;
/* Methods (synchronous) */
char** (*list)(GBinderServiceManager* self);
@@ -86,6 +85,9 @@ typedef struct gbinder_servicemanager_class {
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)
GBinderServiceManager*
gbinder_servicemanager_new_with_type(
@@ -99,6 +101,19 @@ gbinder_servicemanager_service_registered(
const char* name)
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_hidl_get_type(void) GBINDER_INTERNAL;
#endif /* GBINDER_SERVICEMANAGER_PRIVATE_H */
/*

View File

@@ -48,6 +48,7 @@ typedef struct gbinder_ipc_looper_tx GBinderIpcLooperTx;
#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')

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -686,13 +686,9 @@ gbinder_writer_data_append_hidl_vec(
vec->owns_buffer = TRUE;
data->cleanup = gbinder_cleanup_add(data->cleanup, g_free, vec);
/* Write the buffer object pointing to the vector descriptor */
/* Every vector, even the one without data, requires two buffer objects */
gbinder_writer_data_write_buffer_object(data, vec, sizeof(*vec), NULL);
/* Not sure what's the right way to deal with NULL vectors... */
if (buf) {
gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
}
gbinder_writer_data_write_buffer_object(data, buf, total, &vec_parent);
}
void
@@ -732,13 +728,14 @@ gbinder_writer_data_append_hidl_string(
gbinder_writer_data_write_buffer_object(data, hidl_string,
sizeof(*hidl_string), NULL);
/* Not sure what's the right way to deal with NULL strings... */
if (str) {
/* Write the buffer pointing to the string data including the
* NULL terminator, referencing string descriptor as a parent. */
gbinder_writer_data_write_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_write_buffer_object(data, NULL, 0, &str_parent);
}
}
@@ -819,9 +816,16 @@ gbinder_writer_data_append_hidl_string_vec(
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_write_buffer_object(data, NULL, 0,
&str_parent);
}
str_parent.offset += sizeof(GBinderHidlString);
}
} else {
gbinder_writer_data_write_buffer_object(data, NULL, 0, &vec_parent);
}
}

View File

@@ -138,3 +138,17 @@ libgbinder-debug:
libgbinder-release:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)
#
# Install
#
INSTALL = install
INSTALL_BIN_DIR = $(DESTDIR)/usr/bin
install: release $(INSTALL_BIN_DIR)
$(INSTALL) -m 755 $(RELEASE_EXE) $(INSTALL_BIN_DIR)
$(INSTALL_BIN_DIR):
$(INSTALL) -d $@

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -39,7 +39,7 @@
#define RET_INVARG (2)
#define RET_ERR (3)
#define DEV_DEFAULT "/dev/binder"
#define DEV_DEFAULT GBINDER_DEFAULT_HWBINDER
typedef struct app_options {
char* dev;

View File

@@ -138,3 +138,17 @@ libgbinder-debug:
libgbinder-release:
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)
#
# Install
#
INSTALL = install
INSTALL_BIN_DIR = $(DESTDIR)/usr/bin
install: release $(INSTALL_BIN_DIR)
$(INSTALL) -m 755 $(RELEASE_EXE) $(INSTALL_BIN_DIR)
$(INSTALL_BIN_DIR):
$(INSTALL) -d $@

View File

@@ -5,6 +5,7 @@ all:
@$(MAKE) -C unit_buffer $*
@$(MAKE) -C unit_cleanup $*
@$(MAKE) -C unit_client $*
@$(MAKE) -C unit_config $*
@$(MAKE) -C unit_driver $*
@$(MAKE) -C unit_eventloop $*
@$(MAKE) -C unit_ipc $*
@@ -18,6 +19,8 @@ all:
@$(MAKE) -C unit_remote_reply $*
@$(MAKE) -C unit_remote_request $*
@$(MAKE) -C unit_servicemanager $*
@$(MAKE) -C unit_servicemanager_aidl $*
@$(MAKE) -C unit_servicemanager_aidl2 $*
@$(MAKE) -C unit_servicename $*
@$(MAKE) -C unit_servicepoll $*
@$(MAKE) -C unit_writer $*

View File

@@ -44,7 +44,7 @@ COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage
CC ?= $(CROSS_COMPILE)gcc
LD = $(CC)
WARNINGS += -Wall
WARNINGS += -Wall -Wno-deprecated-declarations
INCLUDES += -I$(COMMON_DIR) -I$(LIB_DIR)/src -I$(LIB_DIR)/include
BASE_FLAGS = -fPIC
BASE_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS)

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -30,6 +30,8 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_local_object.h"
#include "gbinder_system.h"
#define GLOG_MODULE_NAME test_binder_log
@@ -47,19 +49,27 @@ static GHashTable* test_fd_map = NULL;
static GHashTable* test_node_map = NULL;
static GPrivate test_looper;
#define public_fd fd[0]
#define private_fd fd[1]
G_LOCK_DEFINE_STATIC(test_binder);
static GCond test_node_map_cond;
#define PUBLIC (0)
#define PRIVATE (1)
#define public_fd node[PUBLIC].fd
#define private_fd node[PRIVATE].fd
#define BINDER_VERSION _IOWR('b', 9, gint32)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, guint32)
#define B_TYPE_LARGE 0x85
#define BINDER_TYPE_BINDER GBINDER_FOURCC('s', 'b', '*', B_TYPE_LARGE)
#define BINDER_TYPE_HANDLE GBINDER_FOURCC('s', 'h', '*', B_TYPE_LARGE)
#define TF_ONE_WAY 0x01
#define TF_ROOT_OBJECT 0x04
#define TF_STATUS_CODE 0x08
#define TF_ACCEPT_FDS 0x10
typedef struct test_binder_io TestBinderIo;
typedef struct test_binder TestBinder;
typedef
void
@@ -77,23 +87,33 @@ typedef struct test_binder_submit_thread {
} TestBinderSubmitThread;
typedef struct test_binder_node {
int fd;
char* path;
int refcount;
const TestBinderIo* io;
GHashTable* destroy_map;
TestBinder* binder;
gboolean looper_enabled;
} TestBinderNode;
typedef struct test_binder {
typedef struct test_binder_fd {
int fd;
GHashTable* destroy_map;
TestBinderNode* node;
} TestBinderFd;
typedef struct test_binder {
gint refcount;
const TestBinderIo* io;
GHashTable* object_map; /* GBinderLocalObject* => handle */
GHashTable* handle_map; /* handle => GBinderLocalObject* */
TestBinderSubmitThread* submit_thread;
gboolean looper_enabled;
int fd[2];
GMutex mutex;
gboolean passthrough;
TestBinderNode node[2];
} TestBinder;
struct test_binder_io {
int version;
int write_read_request;
int (*handle_write_read)(TestBinder* binder, void* data);
int (*handle_write_read)(TestBinderFd* fd, void* data);
};
typedef struct binder_write_read_64 {
@@ -128,6 +148,13 @@ typedef struct binder_handle_cookie_64 {
guint64 cookie;
} __attribute__((packed)) BinderHandleCookie64;
typedef struct binder_object_64 {
guint32 type;
guint32 flags;
guint64 object;
guint64 cookie;
} BinderObject64;
#define BC_TRANSACTION_64 _IOW('c', 0, BinderTransactionData64)
#define BC_REPLY_64 _IOW('c', 1, BinderTransactionData64)
#define BC_FREE_BUFFER_64 _IOW('c', 3, guint64)
@@ -259,74 +286,196 @@ test_binder_submit_later(
static
void
test_io_free_buffer(
TestBinder* binder,
TestBinderFd* fd,
void* ptr)
{
if (ptr) {
TestBinderNode* node = binder->node;
GDestroyNotify destroy = g_hash_table_lookup(node->destroy_map, ptr);
GDestroyNotify destroy;
G_LOCK(test_binder);
destroy = g_hash_table_lookup(fd->destroy_map, ptr);
if (destroy) {
g_hash_table_remove(node->destroy_map, ptr);
g_hash_table_remove(fd->destroy_map, ptr);
destroy(ptr);
} else {
g_free(ptr);
}
G_UNLOCK(test_binder);
}
}
void
test_binder_exit_wait(
void)
{
G_LOCK(test_binder);
while (test_node_map) {
GDEBUG("Waiting for loopers to exit...");
g_cond_wait(&test_node_map_cond, &G_LOCK_NAME(test_binder));
}
G_UNLOCK(test_binder);
}
static
guint64
test_io_passthough_fix_handle(
TestBinder* binder,
guint64 handle)
{
gpointer key = GSIZE_TO_POINTER(handle);
/* Invoked under lock */
if (g_hash_table_contains(binder->object_map, key)) {
gpointer value = g_hash_table_lookup(binder->object_map, key);
handle = GPOINTER_TO_SIZE(value);
GDEBUG("Object %p => handle %u", key, (guint) handle);
} else if (g_hash_table_contains(binder->handle_map, key)) {
gpointer obj = g_hash_table_lookup(binder->handle_map, key);
GDEBUG("Handle %u => object %p", (guint) handle, obj);
handle = GPOINTER_TO_SIZE(obj);
}
return handle;
}
static
gssize
test_io_passthough_write_64(
TestBinderFd* fd,
const void* bytes,
gsize bytes_to_write)
{
const guint code = *(guint32*)bytes;
TestBinderNode* node = fd->node;
TestBinder* binder = node->binder;
BinderTransactionData64* tx = NULL;
gssize bytes_written;
guint extra;
guint32* cmd;
void* data;
/* Just ignore some codes for now */
switch (code) {
case BC_ACQUIRE:
case BC_RELEASE:
case BC_REQUEST_DEATH_NOTIFICATION_64:
case BC_CLEAR_DEATH_NOTIFICATION_64:
return bytes_to_write;
default:
break;
}
cmd = g_memdup(bytes, bytes_to_write);
data = cmd + 1;
switch (*cmd) {
case BR_TRANSACTION_64:
*cmd = BC_TRANSACTION_64;
tx = data;
break;
case BC_TRANSACTION_64:
*cmd = BR_TRANSACTION_64;
tx = data;
break;
case BR_REPLY_64:
*cmd = BC_REPLY_64;
tx = data;
break;
case BC_REPLY_64:
extra = BR_TRANSACTION_COMPLETE;
write(fd->fd, &extra, sizeof(extra));
*cmd = BR_REPLY_64;
tx = data;
break;
}
if (tx) {
guint32* data_buffer = g_memdup(GSIZE_TO_POINTER(tx->data_buffer),
tx->data_size);
void* data_offsets = g_memdup(GSIZE_TO_POINTER(tx->data_offsets),
tx->offsets_size);
G_LOCK(test_binder);
tx->handle = test_io_passthough_fix_handle(binder, tx->handle);
if (data_buffer && tx->data_size > sizeof(BinderObject64)) {
/* Replace BINDER_TYPE_BINDER with BINDER_TYPE_HANDLE */
guint32* data_ptr = data_buffer;
const guint32* data_end = data_buffer + (tx->data_size -
sizeof(BinderObject64))/sizeof(guint32);
/*
* Objects are supposed to be aligned at 32-bit boundary, so we
* can scan the data buffer with 4-byte step.
*/
for (data_ptr = data_buffer; data_ptr < data_end; data_ptr++) {
if (*data_ptr == BINDER_TYPE_BINDER) {
BinderObject64* object = (BinderObject64*) data_ptr;
object->type = BINDER_TYPE_HANDLE;
object->object = test_io_passthough_fix_handle(binder,
object->object);
data_ptr += sizeof(object)/sizeof(guint32) - 1;
}
}
}
g_hash_table_replace(fd->destroy_map, data_offsets, NULL);
G_UNLOCK(test_binder);
tx->data_buffer = GPOINTER_TO_SIZE(data_buffer);
tx->data_offsets = GPOINTER_TO_SIZE(data_offsets);
}
bytes_written = write(fd->fd, cmd, bytes_to_write);
g_free(cmd);
return bytes_written;
}
static
int
test_io_handle_write_read_64(
TestBinder* binder,
TestBinderFd* fd,
void* data)
{
TestBinderNode* node = fd->node;
TestBinder* binder = node->binder;
BinderWriteRead64* wr = data;
gssize bytes_left = wr->write_size - wr->write_consumed;
const guint8* write_ptr = (void*)(gsize)
(wr->write_buffer + wr->write_consumed);
const guint8* write_ptr = (void*)(gsize)(wr->write_buffer +
wr->write_consumed);
gboolean is_looper;
while (bytes_left >= sizeof(guint32)) {
const guint cmd = *(guint32*)write_ptr;
const guint cmdsize = _IOC_SIZE(cmd);
const void* cmddata = write_ptr + sizeof(guint32);
const gsize bytes_to_write = sizeof(guint32) + cmdsize;
GASSERT(bytes_left >= (sizeof(guint32) + cmdsize));
if (bytes_left >= (sizeof(guint32) + cmdsize)) {
wr->write_consumed += sizeof(guint32);
write_ptr += sizeof(guint32);
bytes_left -= sizeof(guint32);
GASSERT(bytes_left >= bytes_to_write);
if (bytes_left >= bytes_to_write) {
gssize bytes_written = bytes_to_write;
switch (cmd) {
case BC_TRANSACTION_64:
case BC_REPLY_64:
/* Is there anything special about transactions and replies? */
break;
case BC_FREE_BUFFER_64:
test_io_free_buffer(binder,
GSIZE_TO_POINTER(*(guint64*)write_ptr));
test_io_free_buffer(fd, GSIZE_TO_POINTER(*(guint64*)cmddata));
break;
case BC_ENTER_LOOPER:
g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
break;
g_private_set(&test_looper, GINT_TO_POINTER(TRUE));
break;
case BC_EXIT_LOOPER:
g_private_set(&test_looper, NULL);
break;
case BC_REQUEST_DEATH_NOTIFICATION_64:
case BC_CLEAR_DEATH_NOTIFICATION_64:
case BC_INCREFS:
case BC_ACQUIRE:
case BC_RELEASE:
case BC_DECREFS:
g_private_set(&test_looper, NULL);
break;
default:
#pragma message("TODO: implement more BINDER_WRITE_READ commands")
GDEBUG("Unhandled command 0x%08x", cmd);
if (binder->passthrough) {
bytes_written = test_io_passthough_write_64(fd,
write_ptr, bytes_to_write);
}
break;
}
wr->write_consumed += cmdsize;
write_ptr += cmdsize;
bytes_left -= cmdsize;
if (bytes_written >= 0) {
wr->write_consumed += bytes_written;
write_ptr += bytes_written;
bytes_left -= bytes_written;
} else {
GDEBUG("Write failed, %s", strerror(errno));
return -1;
}
} else {
/* Partial command in the buffer */
errno = EINVAL;
@@ -335,17 +484,17 @@ test_io_handle_write_read_64(
}
is_looper = g_private_get(&test_looper) ? TRUE : FALSE;
if (binder->looper_enabled || !is_looper) {
if (node->looper_enabled || !is_looper) {
/* Now read the data from the socket */
int bytes_available = 0;
int err = ioctl(binder->public_fd, FIONREAD, &bytes_available);
int err = ioctl(fd->fd, FIONREAD, &bytes_available);
if (err >= 0) {
int bytes_read = 0;
if (bytes_available >= 4) {
bytes_read = read(binder->public_fd,
(void*)(gsize)(wr->read_buffer + wr->read_consumed),
bytes_read = read(fd->fd, (void*)(gsize)
(wr->read_buffer + wr->read_consumed),
wr->read_size - wr->read_consumed);
} else {
struct timespec wait;
@@ -387,26 +536,26 @@ test_binder_ioctl_version(
TestBinder* binder,
int* version)
{
*version = binder->node->io->version;
*version = binder->io->version;
return 0;
}
static
void
test_binder_node_unref(
TestBinderNode* node)
TestBinderFd*
test_binder_fd_from_fd(
int fd)
{
node->refcount--;
if (!node->refcount) {
g_hash_table_remove(test_node_map, node->path);
g_hash_table_destroy(node->destroy_map);
g_free(node->path);
g_free(node);
}
if (!g_hash_table_size(test_node_map)) {
g_hash_table_unref(test_node_map);
test_node_map = NULL;
TestBinderFd* binder_fd = NULL;
G_LOCK(test_binder);
GASSERT(test_fd_map);
if (test_fd_map) {
binder_fd = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
GASSERT(binder_fd);
}
G_UNLOCK(test_binder);
return binder_fd;
}
static
@@ -414,13 +563,9 @@ TestBinder*
test_binder_from_fd(
int fd)
{
TestBinder* binder = NULL;
GASSERT(test_fd_map);
if (test_fd_map) {
binder = g_hash_table_lookup(test_fd_map, GINT_TO_POINTER(fd));
GASSERT(binder);
}
return binder;
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
return binder_fd ? binder_fd->node->binder : NULL;
}
static
@@ -435,11 +580,22 @@ void
test_binder_set_looper_enabled(
int fd,
gboolean enabled)
{
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
g_assert(binder_fd);
binder_fd->node->looper_enabled = enabled;
}
void
test_binder_set_passthrough(
int fd,
gboolean passthrough)
{
TestBinder* binder = test_binder_from_fd(fd);
g_assert(binder);
binder->looper_enabled = enabled;
binder->passthrough = passthrough;
}
void
@@ -448,13 +604,13 @@ test_binder_set_destroy(
gpointer ptr,
GDestroyNotify destroy)
{
TestBinder* binder = test_binder_from_fd(fd);
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
if (binder) {
TestBinderNode* node = binder->node;
g_hash_table_replace(node->destroy_map, ptr,
if (binder_fd) {
G_LOCK(test_binder);
g_hash_table_replace(binder_fd->destroy_map, ptr,
destroy ? destroy : test_io_destroy_none);
G_UNLOCK(test_binder);
}
}
@@ -704,44 +860,220 @@ test_binder_br_reply_status_later(
test_binder_br_reply_status1(fd, status, test_binder_push_data_later);
}
static
void
test_binder_node_clear(
TestBinderNode* node)
{
GDEBUG("Done with %s", node->path);
g_hash_table_remove(test_node_map, node->path);
if (!g_hash_table_size(test_node_map)) {
g_hash_table_unref(test_node_map);
g_cond_broadcast(&test_node_map_cond);
test_node_map = NULL;
}
close(node->fd);
g_free(node->path);
}
static
TestBinder*
test_binder_ref(
TestBinder* binder)
{
if (binder) {
g_atomic_int_inc(&binder->refcount);
}
return binder;
}
static
void
test_binder_unregister_objects_internal(
TestBinder* binder,
gboolean need_lock)
{
GSList* objects = NULL;
GHashTableIter it;
gpointer value;
if (need_lock) {
G_LOCK(test_binder);
}
g_assert(binder);
g_hash_table_iter_init(&it, binder->handle_map);
while (g_hash_table_iter_next(&it, NULL, &value)) {
objects = g_slist_append(objects, value);
}
g_hash_table_remove_all(binder->object_map);
g_hash_table_remove_all(binder->handle_map);
if (need_lock) {
G_UNLOCK(test_binder);
}
/* Unref GBinderLocalObjects outside the lock */
g_slist_free_full(objects, (GDestroyNotify) gbinder_local_object_unref);
}
static
void
test_binder_unref_internal(
TestBinder* binder,
gboolean need_lock)
{
if (binder && g_atomic_int_dec_and_test(&binder->refcount)) {
if (need_lock) {
G_LOCK(test_binder);
}
test_binder_node_clear(binder->node + 0);
test_binder_node_clear(binder->node + 1);
if (need_lock) {
G_UNLOCK(test_binder);
}
test_binder_submit_thread_free(binder->submit_thread);
test_binder_unregister_objects_internal(binder, need_lock);
g_hash_table_destroy(binder->object_map);
g_hash_table_destroy(binder->handle_map);
g_mutex_clear(&binder->mutex);
g_free(binder);
}
}
guint
test_binder_register_object(
int fd,
GBinderLocalObject* obj,
guint h)
{
TestBinder* binder = test_binder_from_fd(fd);
g_assert(binder);
g_assert(obj);
G_LOCK(test_binder);
g_assert(!g_hash_table_contains(binder->object_map, obj));
g_assert(!g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)));
if (h == AUTO_HANDLE) {
h = 1;
while (g_hash_table_contains(binder->handle_map, GINT_TO_POINTER(h)) ||
g_hash_table_contains(binder->object_map, GINT_TO_POINTER(h))) {
h++;
}
}
GDEBUG("Object %p <=> handle %u", obj, h);
g_hash_table_insert(binder->handle_map, GINT_TO_POINTER(h), obj);
g_hash_table_insert(binder->object_map, obj, GINT_TO_POINTER(h));
G_UNLOCK(test_binder);
gbinder_local_object_ref(obj);
return h;
}
void
test_binder_unregister_objects(
int fd)
{
test_binder_unregister_objects_internal(test_binder_from_fd(fd), TRUE);
}
static
void
test_fd_map_free(
gpointer entry)
{
TestBinderFd* binder_fd = entry;
GHashTableIter it;
gpointer key, value;
g_hash_table_iter_init(&it, binder_fd->destroy_map);
while (g_hash_table_iter_next(&it, &key, &value)) {
if (value) {
((GDestroyNotify)value)(key);
} else {
g_free(key);
}
}
g_hash_table_destroy(binder_fd->destroy_map);
test_binder_unref_internal(binder_fd->node->binder, FALSE);
close(binder_fd->fd);
g_free(binder_fd);
}
static
TestBinderFd*
test_binder_fd_new(
TestBinderNode* node)
{
TestBinderFd* binder_fd = g_new0(TestBinderFd, 1);
test_binder_ref(node->binder);
binder_fd->node = node;
binder_fd->fd = dup(node->fd);
binder_fd->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
return binder_fd;
}
int
gbinder_system_open(
const char* path,
int flags)
{
if (path && g_str_has_prefix(path, "/dev") &&
g_str_has_suffix(path, "binder")) {
TestBinderNode* node = NULL;
TestBinder* binder = NULL;
int fd;
static const char binder_suffix[] = "binder";
static const char binder_private_suffix[] = "binder-private";
static const char private_suffix[] = "-private";
if (path && g_str_has_prefix(path, "/dev/") &&
(g_str_has_suffix(path, binder_suffix) ||
g_str_has_suffix(path, binder_private_suffix))) {
TestBinderFd* fd;
TestBinderNode* node;
G_LOCK(test_binder);
node = test_node_map ? g_hash_table_lookup(test_node_map, path) : NULL;
if (!node) {
int i, fds[2];
TestBinder* binder = g_new0(TestBinder, 1);
binder->io = &test_io_64;
g_mutex_init(&binder->mutex);
g_assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
if (g_str_has_suffix(path, binder_suffix)) {
node = binder->node + PUBLIC;
node->path = g_strdup(path);
binder->node[PRIVATE].path = g_strconcat(path,
private_suffix, NULL);
} else {
node = binder->node + PRIVATE;
node->path = g_strdup(path);
binder->node[PUBLIC].path = g_strndup(path,
strlen(path) - strlen(private_suffix) - 1);
}
if (test_node_map) {
node = g_hash_table_lookup(test_node_map, path);
}
if (node) {
node->refcount++;
} else {
node = g_new0(TestBinderNode, 1);
node->path = g_strdup(path);
node->refcount = 1;
node->io = &test_io_64;
node->destroy_map = g_hash_table_new(g_direct_hash, g_direct_equal);
if (!test_node_map) {
test_node_map = g_hash_table_new(g_str_hash, g_str_equal);
}
g_hash_table_replace(test_node_map, node->path, node);
for (i = 0; i < 2; i++) {
binder->node[i].binder = binder;
binder->node[i].fd = fds[i];
g_hash_table_replace(test_node_map, binder->node[i].path,
binder->node + i);
}
binder->object_map = g_hash_table_new
(g_direct_hash, g_direct_equal);
binder->handle_map = g_hash_table_new
(g_direct_hash, g_direct_equal);
GDEBUG("Created %s <=> %s binder", binder->node[0].path,
binder->node[1].path);
}
binder = g_new0(TestBinder, 1);
binder->node = node;
socketpair(AF_UNIX, SOCK_STREAM, 0, binder->fd);
fd = binder->public_fd;
fd = test_binder_fd_new(node);
if (!test_fd_map) {
test_fd_map = g_hash_table_new(g_direct_hash, g_direct_equal);
test_fd_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, test_fd_map_free);
}
g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd), binder);
return fd;
g_hash_table_replace(test_fd_map, GINT_TO_POINTER(fd->fd), fd);
G_UNLOCK(test_binder);
return fd->fd;
} else {
errno = ENOENT;
return -1;
@@ -752,23 +1084,22 @@ int
gbinder_system_close(
int fd)
{
TestBinder* binder = test_binder_from_fd(fd);
int ret;
if (binder) {
g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd));
G_LOCK(test_binder);
if (g_hash_table_remove(test_fd_map, GINT_TO_POINTER(fd))) {
if (!g_hash_table_size(test_fd_map)) {
g_hash_table_unref(test_fd_map);
test_fd_map = NULL;
}
test_binder_submit_thread_free(binder->submit_thread);
test_binder_node_unref(binder->node);
close(binder->public_fd);
close(binder->private_fd);
g_free(binder);
return 0;
ret = 0;
} else {
errno = EBADF;
ret = -1;
}
errno = EBADF;
return -1;
G_UNLOCK(test_binder);
return ret;
}
int
@@ -777,9 +1108,11 @@ gbinder_system_ioctl(
int request,
void* data)
{
TestBinder* binder = test_binder_from_fd(fd);
if (binder) {
const TestBinderIo* io = binder->node->io;
TestBinderFd* binder_fd = test_binder_fd_from_fd(fd);
if (binder_fd) {
TestBinder* binder = binder_fd->node->binder;
const TestBinderIo* io = binder->io;
switch (request) {
case BINDER_VERSION:
@@ -788,7 +1121,7 @@ gbinder_system_ioctl(
return 0;
default:
if (request == io->write_read_request) {
return io->handle_write_read(binder, data);
return io->handle_write_read(binder_fd, data);
} else {
errno = EINVAL;
return -1;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -35,6 +35,8 @@
#include "test_common.h"
typedef struct test_binder TestBinder;
void
test_binder_br_noop(
int fd);
@@ -116,12 +118,33 @@ test_binder_set_looper_enabled(
int fd,
gboolean enabled);
void
test_binder_set_passthrough(
int fd,
gboolean passthrough);
guint
test_binder_register_object(
int fd,
GBinderLocalObject* obj,
guint handle);
#define AUTO_HANDLE ((guint)-1)
void
test_binder_unregister_objects(
int fd);
void
test_binder_set_destroy(
int fd,
gpointer ptr,
GDestroyNotify destroy);
void
test_binder_exit_wait(
void);
#endif /* TEST_BINDER_H */
/*

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -13,9 +13,9 @@
* 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.
* 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
@@ -58,6 +58,12 @@ void
test_quit_later(
GMainLoop* loop);
/* Quits the event loop after n iterations */
void
test_quit_later_n(
GMainLoop* loop,
guint n);
#define TEST_TIMEOUT_SEC (20)
/* Helper macros */

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -13,9 +13,9 @@
* 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.
* 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
@@ -34,6 +34,11 @@
#include <gutil_log.h>
typedef struct test_quit_later_data{
GMainLoop* loop;
guint n;
} TestQuitLaterData;
static
gboolean
test_timeout_expired(
@@ -43,6 +48,46 @@ test_timeout_expired(
return G_SOURCE_REMOVE;
}
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(

View File

@@ -1,3 +1,4 @@
#!/bin/bash
#
# This script requires lcov, dirname
@@ -7,6 +8,7 @@ TESTS="\
unit_buffer \
unit_cleanup \
unit_client \
unit_config \
unit_driver \
unit_eventloop \
unit_ipc \
@@ -20,6 +22,8 @@ unit_remote_object \
unit_remote_reply \
unit_remote_request \
unit_servicemanager \
unit_servicemanager_aidl \
unit_servicemanager_aidl2 \
unit_servicename \
unit_servicepoll \
unit_writer"

View File

@@ -54,7 +54,7 @@ test_client_new(
guint handle,
const char* iface)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, handle);
GBinderClient* client = gbinder_client_new(obj, iface);
@@ -97,7 +97,7 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
const char* iface = "foo";
@@ -123,7 +123,7 @@ void
test_interfaces(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj = gbinder_object_registry_get_remote(reg, 0);
static const GBinderClientIfaceInfo ifaces[] = {
@@ -208,6 +208,7 @@ test_dead(
gbinder_client_unref(client);
g_main_loop_unref(loop);
gbinder_ipc_exit();
}
/*==========================================================================*

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_config
include ../common/Makefile

View File

@@ -0,0 +1,505 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_common.h"
#include "gbinder_config.h"
#include <gutil_strv.h>
#include <gutil_log.h>
#include <sys/stat.h>
#include <sys/types.h>
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* 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";
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(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/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");
/* 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/hwbinder",b), == ,"hidl");
g_assert_cmpstr(test_value(k,"ServiceManager","/dev/binder",b),==,"aidl2");
/* 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,"ServiceManager","/dev/binder",b),==,"aidl2");
/* 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,"ServiceManager","/dev/binder",b),==,"aidl2");
/* Delete the remaining files and try again */
gbinder_config_exit();
g_assert_cmpint(remove(file1), == ,0);
g_assert_cmpint(remove(file2), == ,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(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"
}
};
/*==========================================================================*
* 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:
*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -109,8 +109,8 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderIpc* ipc2 = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
g_assert(ipc);
g_assert(ipc2);
@@ -119,16 +119,17 @@ test_basic(
gbinder_ipc_unref(ipc2);
/* Second gbinder_ipc_new returns the same (default) object */
g_assert(gbinder_ipc_new(NULL, NULL) == ipc);
g_assert(gbinder_ipc_new("", NULL) == ipc);
g_assert(gbinder_ipc_new(NULL) == ipc);
g_assert(gbinder_ipc_new("") == ipc);
gbinder_ipc_unref(ipc);
gbinder_ipc_unref(ipc);
gbinder_ipc_unref(ipc);
/* Invalid path */
g_assert(!gbinder_ipc_new("invalid path", NULL));
g_assert(!gbinder_ipc_new("invalid path"));
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -153,7 +154,7 @@ void
test_async_oneway(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -180,7 +181,7 @@ void
test_sync_oneway(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -191,6 +192,7 @@ test_sync_oneway(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -202,7 +204,7 @@ void
test_sync_reply_ok_status(
int* status)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -235,6 +237,7 @@ test_sync_reply_ok_status(
gbinder_local_reply_unref(reply);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
static
@@ -258,7 +261,7 @@ void
test_sync_reply_error(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -278,6 +281,7 @@ test_sync_reply_error(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
}
/*==========================================================================*
@@ -316,7 +320,7 @@ void
test_transact_ok(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -348,6 +352,7 @@ test_transact_ok(
gbinder_local_reply_unref(reply);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -374,7 +379,7 @@ void
test_transact_dead(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -395,6 +400,7 @@ test_transact_dead(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -421,7 +427,7 @@ void
test_transact_failed(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -442,6 +448,7 @@ test_transact_failed(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -470,7 +477,7 @@ void
test_transact_status(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
@@ -491,6 +498,7 @@ test_transact_status(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -512,7 +520,7 @@ void
test_transact_custom(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_ipc_transact_custom(ipc, NULL,
test_transact_custom_done, NULL, loop);
@@ -523,6 +531,7 @@ test_transact_custom(
gbinder_ipc_exit();
gbinder_ipc_unref(ipc);
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
@@ -543,7 +552,7 @@ void
test_transact_custom2(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id = gbinder_ipc_transact_custom(ipc, NULL, NULL,
test_transact_custom_destroy, loop);
@@ -554,6 +563,7 @@ test_transact_custom2(
gbinder_ipc_exit();
gbinder_ipc_unref(ipc);
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
@@ -575,7 +585,7 @@ void
test_transact_custom3(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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,
@@ -586,6 +596,7 @@ test_transact_custom3(
/* Reference to GBinderIpc is released by test_transact_custom3_exec */
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -624,7 +635,7 @@ void
test_transact_cancel(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -635,6 +646,7 @@ test_transact_cancel(
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -669,7 +681,7 @@ void
test_transact_cancel2(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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,
@@ -680,9 +692,99 @@ test_transact_cancel2(
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
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(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
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 = gbinder_local_request_new(io, NULL);
GBinderLocalRequest* incoming_req = gbinder_local_request_new(io, NULL);
GBinderLocalReply* reply = gbinder_local_reply_new(io);
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_br_transaction(fd, obj, 2,
gbinder_local_request_data(req)->bytes);
test_binder_br_noop(fd);
test_binder_br_transaction_complete(fd);
test_binder_br_noop(fd);
test_binder_br_reply(fd, 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);
gbinder_ipc_exit();
g_main_loop_unref(loop);
test_binder_exit_wait();
}
/*==========================================================================*
* transact_incoming
*==========================================================================*/
@@ -715,7 +817,7 @@ void
test_transact_incoming(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
@@ -724,16 +826,21 @@ test_transact_incoming(
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new
(ipc, ifaces, test_transact_incoming_proc, loop);
GBinderLocalRequest* ping = gbinder_local_request_new(io, NULL);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GBinderOutputData* data;
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");
data = gbinder_local_request_data(req);
test_binder_br_transaction(fd, obj, 1, data->bytes);
test_binder_br_transaction(fd, obj, prot->ping_tx,
gbinder_local_request_data(ping)->bytes);
test_binder_br_transaction(fd, obj, 1,
gbinder_local_request_data(req)->bytes);
test_binder_set_looper_enabled(fd, TRUE);
test_run(&test_opt, loop);
@@ -741,11 +848,13 @@ test_transact_incoming(
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);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -779,7 +888,7 @@ void
test_transact_status_reply(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
@@ -810,6 +919,7 @@ test_transact_status_reply(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -886,7 +996,7 @@ void
test_transact_async(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
@@ -917,6 +1027,7 @@ test_transact_async(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -959,7 +1070,7 @@ void
test_transact_async_sync(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
const int fd = gbinder_driver_fd(ipc->driver);
const char* dev = gbinder_driver_dev(ipc->driver);
@@ -990,6 +1101,7 @@ test_transact_async_sync(
test_run(&test_opt, loop);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1013,7 +1125,7 @@ void
test_drop_remote_refs(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderLocalObject* obj = gbinder_local_object_new
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -1032,6 +1144,7 @@ test_drop_remote_refs(
/* gbinder_ipc_exit will drop the remote reference */
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1055,7 +1168,7 @@ void
test_cancel_on_exit(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const GBinderIo* io = gbinder_driver_io(ipc->driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -1069,6 +1182,7 @@ test_cancel_on_exit(
gbinder_local_request_unref(req);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
@@ -1097,6 +1211,7 @@ int main(int argc, char* argv[])
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_status_reply"), test_transact_status_reply);
g_test_add_func(TEST_("transact_async"), test_transact_async);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -152,7 +152,7 @@ test_basic(
{
const char* const ifaces_foo[] = { "foo", NULL };
const char* const ifaces_bar[] = { "bar", NULL };
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderLocalObject* foo;
GBinderLocalObject* bar;
@@ -201,7 +201,7 @@ test_ping(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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);
@@ -247,7 +247,7 @@ test_interface(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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);
@@ -296,7 +296,7 @@ test_hidl_ping(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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);
@@ -346,7 +346,7 @@ test_get_descriptor(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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);
@@ -402,7 +402,7 @@ test_descriptor_chain(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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);
@@ -476,7 +476,7 @@ test_custom_iface(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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,
@@ -583,7 +583,7 @@ test_reply_status(
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);
GBinderIpc* ipc = gbinder_ipc_new(dev);
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,
@@ -624,7 +624,7 @@ void
test_increfs(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderLocalObject* obj = gbinder_local_object_new
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -642,6 +642,7 @@ test_increfs(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -666,7 +667,7 @@ void
test_decrefs(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderLocalObject* obj = gbinder_local_object_new
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -685,6 +686,7 @@ test_decrefs(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -708,7 +710,7 @@ void
test_acquire(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderLocalObject* obj = gbinder_local_object_new
(ipc, NULL, NULL, NULL);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
@@ -726,6 +728,7 @@ test_acquire(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -750,7 +753,7 @@ void
test_release(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
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);
@@ -768,6 +771,7 @@ test_release(
gbinder_local_object_remove_handler(obj, id);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -357,10 +357,10 @@ test_hidl_string(
gbinder_local_reply_append_hidl_string(reply, NULL);
data = gbinder_local_reply_data(reply);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets->count == 1);
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 == BUFFER_OBJECT_SIZE_32);
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_reply_unref(reply);
}
@@ -380,10 +380,10 @@ test_hidl_string_vec(
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 == 1);
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 == BUFFER_OBJECT_SIZE_32);
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_reply_unref(reply);
}
@@ -399,7 +399,7 @@ test_local_object(
GBinderLocalReply* reply;
GBinderOutputData* data;
GUtilIntArray* offsets;
GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL);
GBinderIpc* ipc = gbinder_ipc_new(NULL);
const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL };
GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -385,10 +385,10 @@ test_hidl_string(
gbinder_local_request_append_hidl_string(req, NULL);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets->count == 1);
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 == BUFFER_OBJECT_SIZE_32);
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
}
@@ -408,10 +408,10 @@ test_hidl_string_vec(
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 == 1);
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 == BUFFER_OBJECT_SIZE_32);
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -33,6 +33,7 @@
#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"
@@ -43,36 +44,118 @@
#include "gbinder_writer.h"
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)
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_binder [] = {
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_hwbinder [] = {
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_hidl [] = {
'f', 'o', 'o', 0x00
};
static const TestHeaderData test_header_tests[] = {
{ "binder", GBINDER_DEFAULT_BINDER, "foo",
TEST_ARRAY_AND_SIZE(test_header_binder) },
{ "hwbinder", GBINDER_DEFAULT_HWBINDER, "foo",
TEST_ARRAY_AND_SIZE(test_header_hwbinder) }
{ "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 */
{ "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_config {
char* dir;
char* file;
} TestConfig;
static
void
test_config_init(
TestConfig* test,
const char* config)
{
test->dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
test->file = g_build_filename(test->dir, "test.conf", NULL);
/* Reset the state */
gbinder_rpc_protocol_exit();
gbinder_config_exit();
/* Write the config */
g_assert(g_file_set_contents(test->file, config, -1, NULL));
gbinder_config_file = test->file;
}
static
void
test_config_init2(
TestConfig* test,
const char* dev,
const char* prot)
{
char* config = g_strconcat("[Protocol]\n", dev, " = ", prot, "\n", NULL);
test_config_init(test, config);
g_free(config);
}
static
void
test_config_cleanup(
TestConfig* test)
{
/* Undo the damage */
gbinder_rpc_protocol_exit();
gbinder_config_exit();
gbinder_config_file = NULL;
remove(test->file);
g_free(test->file);
remove(test->dir);
g_free(test->dir);
}
/*==========================================================================*
* device
*==========================================================================*/
@@ -82,12 +165,129 @@ void
test_device(
void)
{
g_assert(gbinder_rpc_protocol_for_device(NULL) ==
&gbinder_rpc_protocol_binder);
g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER) ==
&gbinder_rpc_protocol_binder);
g_assert(gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_HWBINDER) ==
&gbinder_rpc_protocol_hwbinder);
const GBinderRpcProtocol* p;
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");
}
/*==========================================================================*
* config1
*==========================================================================*/
static
void
test_config1(
void)
{
const GBinderRpcProtocol* p;
TestConfig config;
test_config_init(&config,
"[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_config_cleanup(&config);
}
/*==========================================================================*
* config2
*==========================================================================*/
static
void
test_config2(
void)
{
const GBinderRpcProtocol* p;
TestConfig config;
test_config_init(&config,
"[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_config_cleanup(&config);
}
/*==========================================================================*
* config3
*==========================================================================*/
static
void
test_config3(
void)
{
const GBinderRpcProtocol* p;
TestConfig config;
test_config_init(&config,
"[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_config_cleanup(&config);
}
/*==========================================================================*
@@ -97,14 +297,21 @@ test_device(
static
void
test_no_header1(
void)
gconstpointer test_data)
{
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
gbinder_rpc_protocol_for_device(GBINDER_DEFAULT_BINDER), 0, 0);
const TestData* test = test_data;
GBinderRemoteRequest* req;
TestConfig config;
test_config_init2(&config, 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_config_cleanup(&config);
}
/*==========================================================================*
@@ -114,21 +321,35 @@ test_no_header1(
static
void
test_no_header2(
void)
gconstpointer test_data)
{
const GBinderRpcProtocol* p = &gbinder_rpc_protocol_binder;
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, p);
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL, p, 0, 0);
const TestData* test = test_data;
const GBinderRpcProtocol* p;
GBinderDriver* driver;
GBinderRemoteRequest* req;
TestConfig config;
test_config_init2(&config, 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_binder)),
sizeof(test_header_binder), NULL));
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_config_cleanup(&config);
}
static const TestData test_no_header_data[] = {
{ "aidl", "aidl", GBINDER_DEFAULT_BINDER },
{ "aidl2", "aidl2", GBINDER_DEFAULT_BINDER },
};
/*==========================================================================*
* write_header
*==========================================================================*/
@@ -139,17 +360,24 @@ test_write_header(
gconstpointer test_data)
{
const TestHeaderData* test = test_data;
const GBinderRpcProtocol* prot = gbinder_rpc_protocol_for_device(test->dev);
GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
const GBinderRpcProtocol* prot;
GBinderLocalRequest* req;
GBinderOutputData* data;
GBinderWriter writer;
TestConfig config;
test_config_init2(&config, test->dev, test->prot);
prot = gbinder_rpc_protocol_for_device(test->dev);
req = gbinder_local_request_new(&gbinder_io_32, 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_config_cleanup(&config);
}
/*==========================================================================*
@@ -162,16 +390,23 @@ test_read_header(
gconstpointer test_data)
{
const TestHeaderData* test = test_data;
GBinderDriver* driver = gbinder_driver_new(test->dev, NULL);
GBinderRemoteRequest* req = gbinder_remote_request_new(NULL,
gbinder_rpc_protocol_for_device(test->dev), 0, 0);
GBinderDriver* driver;
GBinderRemoteRequest* req;
TestConfig config;
test_config_init2(&config, 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(!g_strcmp0(gbinder_remote_request_interface(req), test->iface));
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,test->iface);
gbinder_remote_request_unref(req);
gbinder_driver_unref(driver);
test_config_cleanup(&config);
}
/*==========================================================================*
@@ -187,20 +422,36 @@ int main(int argc, char* argv[])
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("device"), test_device);
g_test_add_func(TEST_("no_header1"), test_no_header1);
g_test_add_func(TEST_("no_header2"), test_no_header2);
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_PREFIX, test->name, "/read_header", NULL);
path = g_strconcat(TEST_("read_header/"), test->name, NULL);
g_test_add_data_func(path, test, test_read_header);
g_free(path);
path = g_strconcat(TEST_PREFIX, test->name, "/write_header", NULL);
g_test_add_data_func(path, test, test_write_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);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -624,7 +624,7 @@ test_hidl_struct(
gconstpointer test_data)
{
const TestHidlStruct* test = test_data;
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(test->in, test->in_size), test->in_size, NULL);
GBinderReaderData data;
@@ -828,7 +828,7 @@ test_hidl_vec(
gconstpointer test_data)
{
const TestHidlVec* test = test_data;
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(test->in, test->in_size), test->in_size, NULL);
GBinderReaderData data;
@@ -934,7 +934,7 @@ test_hidl_string_err(
gconstpointer test_data)
{
const TestHidlStringErr* test = test_data;
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(test->in, test->in_size), test->in_size, NULL);
GBinderReaderData data;
@@ -968,7 +968,7 @@ test_hidl_string_err_skip(
gconstpointer test_data)
{
const TestHidlStringErr* test = test_data;
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(test->in, test->in_size), test->in_size, NULL);
GBinderReaderData data;
@@ -1013,7 +1013,7 @@ test_fd_ok(
TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1053,7 +1053,7 @@ test_fd_shortbuf(
TEST_INT32_BYTES(BINDER_TYPE_FD),
TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1087,7 +1087,7 @@ test_fd_badtype(
TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1130,7 +1130,7 @@ test_dupfd_ok(
TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1177,7 +1177,7 @@ test_dupfd_badtype(
TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1220,7 +1220,7 @@ test_dupfd_badfd(
TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderReaderData data;
@@ -1259,7 +1259,7 @@ test_hidl_string(
guint bufcount,
const char* result)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size),
size, NULL);
GBinderRemoteObject* obj = NULL;
@@ -1530,7 +1530,7 @@ test_buffer(
TEST_INT64_BYTES(0), TEST_INT64_BYTES(0),
TEST_INT64_BYTES(0), TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderRemoteObject* obj = NULL;
@@ -1578,7 +1578,7 @@ test_object(
TEST_INT32_BYTES(BINDER_TYPE_HANDLE), TEST_INT32_BYTES(0),
TEST_INT64_BYTES(1 /* handle*/), TEST_INT64_BYTES(0)
};
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderRemoteObject* obj = NULL;
@@ -1636,7 +1636,7 @@ test_object_invalid(
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);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver,
g_memdup(input, sizeof(input)), sizeof(input), NULL);
GBinderRemoteObject* obj = NULL;
@@ -1670,7 +1670,7 @@ test_vec(
void)
{
/* Using 64-bit I/O */
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderReaderData data;
GBinderReader reader;
BinderObject64 obj;
@@ -1723,7 +1723,7 @@ test_hidl_string_vec(
gsize size,
const char* const* result)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER);
GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, g_memdup(input, size),
size, NULL);
GBinderRemoteObject* obj = NULL;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -68,7 +68,7 @@ void
test_basic(
void)
{
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
GBinderObjectRegistry* reg = gbinder_ipc_object_registry(ipc);
GBinderRemoteObject* obj1 = gbinder_object_registry_get_remote(reg, 1);
GBinderRemoteObject* obj2 = gbinder_object_registry_get_remote(reg, 2);
@@ -112,7 +112,7 @@ test_dead(
{
const guint handle = 1;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER, NULL);
GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_BINDER);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderRemoteObject* obj = gbinder_ipc_get_remote_object
(ipc, handle, FALSE);
@@ -128,6 +128,7 @@ test_dead(
gbinder_remote_object_remove_handler(obj, 0); /* has no effect */
gbinder_remote_object_unref(obj);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -34,6 +34,7 @@
#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"
@@ -47,6 +48,7 @@
#include <errno.h>
static TestOpt test_opt;
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-servicemanager-XXXXXX";
static
void
@@ -224,7 +226,7 @@ test_hwservicemanager_check_name(
{
TestHwServiceManager* self = TEST_HWSERVICEMANAGER(sm);
return (!name || self->reject_name) ?
return (!name || self->reject_name) ?
GBINDER_SERVICEMANAGER_NAME_INVALID :
GBINDER_SERVICEMANAGER_NAME_NORMALIZE;
}
@@ -281,7 +283,6 @@ test_hwservicemanager_class_init(
{
klass->iface = TEST_HWSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_hwbinder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
@@ -292,12 +293,10 @@ test_hwservicemanager_class_init(
G_OBJECT_CLASS(klass)->finalize = test_hwservicemanager_finalize;
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
GType
gbinder_servicemanager_hidl_get_type()
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_HWSERVICEMANAGER,
dev);
return TEST_TYPE_HWSERVICEMANAGER;
}
/*==========================================================================*
@@ -325,7 +324,7 @@ test_defservicemanager_check_name(
{
TestDefServiceManager* self = TEST_DEFSERVICEMANAGER(sm);
return (!name || self->reject_name) ?
return (!name || self->reject_name) ?
GBINDER_SERVICEMANAGER_NAME_INVALID :
GBINDER_SERVICEMANAGER_NAME_OK;
}
@@ -365,7 +364,6 @@ test_defservicemanager_class_init(
{
klass->iface = TEST_DEFSERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_BINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
@@ -374,12 +372,17 @@ test_defservicemanager_class_init(
G_OBJECT_CLASS(klass)->finalize = test_defservicemanager_finalize;
}
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
GType
gbinder_servicemanager_aidl_get_type()
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_DEFSERVICEMANAGER,
dev);
return TEST_TYPE_DEFSERVICEMANAGER;
}
GType
gbinder_servicemanager_aidl2_get_type()
{
/* Avoid pulling in gbinder_servicemanager_aidl2 object */
return TEST_TYPE_DEFSERVICEMANAGER;
}
/*==========================================================================*
@@ -391,6 +394,7 @@ void
test_null(
void)
{
g_assert(!gbinder_servicemanager_new(NULL));
g_assert(!gbinder_servicemanager_new_with_type(0, NULL));
g_assert(!gbinder_servicemanager_new_local_object(NULL, NULL, NULL, NULL));
g_assert(!gbinder_servicemanager_ref(NULL));
@@ -423,7 +427,7 @@ test_invalid(
{
int status = 0;
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServiceManager* sm;
gulong id = 0;
@@ -466,9 +470,9 @@ static
void
test_basic(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServiceManager* sm;
GBinderLocalObject* obj;
@@ -486,6 +490,122 @@ test_basic(
gbinder_ipc_unref(ipc);
}
/*==========================================================================*
* legacy
*==========================================================================*/
static
void
test_legacy(
void)
{
const char* otherdev = "/dev/otherbinder";
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServiceManager* sm;
/* Reset the state */
gbinder_servicemanager_exit();
gbinder_config_exit();
gbinder_config_file = 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);
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();
}
/*==========================================================================*
* config
*==========================================================================*/
static
void
test_config(
void)
{
GBinderIpc* ipc;
GBinderServiceManager* sm;
const char* strange_name = "/dev/notbinder";
const char* legacy_name = "/dev/legacybinder";
char* dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char* file = g_build_filename(dir, "test.conf", NULL);
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";
/* Reset the state */
gbinder_servicemanager_exit();
gbinder_config_exit();
/* Write the config file */
g_assert(g_file_set_contents(file, config, -1, NULL));
gbinder_config_file = file;
/* Unknown device instantiates the default */
ipc = gbinder_ipc_new(strange_name);
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);
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);
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);
test_setup_ping(ipc);
sm = gbinder_servicemanager_new(legacy_name);
g_assert(TEST_IS_DEFSERVICEMANAGER(sm));
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
/* Clear the state */
gbinder_servicemanager_exit();
gbinder_config_exit();
gbinder_config_file = NULL;
remove(file);
remove(dir);
g_free(file);
g_free(dir);
}
/*==========================================================================*
* not_present
*==========================================================================*/
@@ -494,9 +614,9 @@ static
void
test_not_present(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderServiceManager* sm;
@@ -519,9 +639,9 @@ static
void
test_wait(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
const glong forever = (test_opt.flags & TEST_FLAG_DEBUG) ?
(TEST_TIMEOUT_SEC * 1000) : -1;
@@ -569,9 +689,9 @@ static
void
test_wait_long(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderServiceManager* sm;
gulong id;
@@ -614,9 +734,9 @@ static
void
test_wait_async(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
@@ -658,9 +778,9 @@ static
void
test_death(
void)
{
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
@@ -695,6 +815,7 @@ test_death(
gbinder_servicemanager_remove_all_handlers(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -728,7 +849,7 @@ test_reanimate(
void)
{
const char* dev = GBINDER_DEFAULT_HWBINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
@@ -766,6 +887,7 @@ test_reanimate(
gbinder_servicemanager_remove_all_handlers(sm, id);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -777,13 +899,13 @@ 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 = gbinder_ipc_new(binder_dev, NULL);
GBinderIpc* vndbinder_ipc = gbinder_ipc_new(vndbinder_dev, NULL);
GBinderIpc* hwbinder_ipc = gbinder_ipc_new(hwbinder_dev, NULL);
GBinderIpc* binder_ipc = gbinder_ipc_new(binder_dev);
GBinderIpc* vndbinder_ipc = gbinder_ipc_new(vndbinder_dev);
GBinderIpc* hwbinder_ipc = gbinder_ipc_new(hwbinder_dev);
GBinderServiceManager* m1;
GBinderServiceManager* m2;
GBinderServiceManager* vnd1;
@@ -835,7 +957,7 @@ test_notify_type(
GType t,
const char* dev)
{
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServiceManager* sm;
TestHwServiceManager* test;
const char* name = "foo";
@@ -902,7 +1024,7 @@ test_list(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
TestHwServiceManager* test;
@@ -950,7 +1072,7 @@ test_get(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
TestHwServiceManager* test;
@@ -1011,7 +1133,7 @@ test_add(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServiceManager* sm;
TestHwServiceManager* test;
@@ -1048,6 +1170,8 @@ int main(int argc, char* argv[])
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);

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_aidl
include ../common/Makefile

View File

@@ -0,0 +1,581 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_driver.h"
#include "gbinder_ipc.h"
#include "gbinder_reader.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object.h"
#include <gutil_strv.h>
#include <gutil_log.h>
static TestOpt test_opt;
GType
gbinder_servicemanager_hidl_get_type()
{
/* Avoid pulling in gbinder_servicemanager_hidl object */
return 0;
}
GType
gbinder_servicemanager_aidl2_get_type()
{
/* Avoid pulling in gbinder_servicemanager_aidl2 object */
return 0;
}
/*==========================================================================*
* Test service manager
*==========================================================================*/
#define SVCMGR_HANDLE (0)
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
enum servicemanager_aidl_tx {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION
};
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
typedef GBinderLocalObjectClass ServiceManagerAidlClass;
typedef struct service_manager_aidl {
GBinderLocalObject parent;
GHashTable* objects;
gboolean handle_on_looper_thread;
} ServiceManagerAidl;
#define SERVICE_MANAGER_AIDL_TYPE (service_manager_aidl_get_type())
#define SERVICE_MANAGER_AIDL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
SERVICE_MANAGER_AIDL_TYPE, ServiceManagerAidl))
G_DEFINE_TYPE(ServiceManagerAidl, service_manager_aidl, \
GBINDER_TYPE_LOCAL_OBJECT)
static
GBinderLocalReply*
servicemanager_aidl_handler(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
ServiceManagerAidl* self = user_data;
GBinderLocalReply* reply = NULL;
GBinderReader reader;
GBinderRemoteObject* remote_obj;
guint32 num;
char* str;
g_assert(!flags);
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
*status = -1;
switch (code) {
case GET_SERVICE_TRANSACTION:
case CHECK_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
if (str) {
reply = gbinder_local_object_new_reply(obj);
remote_obj = g_hash_table_lookup(self->objects, str);
if (remote_obj) {
GDEBUG("Found name '%s' => %p", str, remote_obj);
gbinder_local_reply_append_remote_object(reply, remote_obj);
} else {
GDEBUG("Name '%s' not found", str);
gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
}
g_free(str);
}
break;
case ADD_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
remote_obj = gbinder_reader_read_object(&reader);
if (str && remote_obj && gbinder_reader_read_uint32(&reader, &num)) {
GDEBUG("Adding '%s'", str);
g_hash_table_replace(self->objects, str, remote_obj);
remote_obj = NULL;
str = NULL;
reply = gbinder_local_object_new_reply(obj);
*status = GBINDER_STATUS_OK;
}
g_free(str);
gbinder_remote_object_unref(remote_obj);
break;
case LIST_SERVICES_TRANSACTION:
if (gbinder_remote_request_read_uint32(req, &num)) {
if (num < g_hash_table_size(self->objects)) {
GList* keys = g_hash_table_get_keys(self->objects);
GList* l = g_list_nth(keys, num);
reply = gbinder_local_object_new_reply(obj);
gbinder_local_reply_append_string16(reply, l->data);
g_list_free(keys);
*status = GBINDER_STATUS_OK;
} else {
GDEBUG("Index %u out of bounds", num);
}
}
break;
default:
GDEBUG("Unhandled command %u", code);
break;
}
return reply;
}
static
ServiceManagerAidl*
servicemanager_aidl_new(
const char* dev,
gboolean handle_on_looper_thread)
{
ServiceManagerAidl* self = g_object_new(SERVICE_MANAGER_AIDL_TYPE, NULL);
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
self->handle_on_looper_thread = handle_on_looper_thread;
gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
servicemanager_aidl_handler, self);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
return self;
}
static
void
servicemanager_aidl_free(
ServiceManagerAidl* self)
{
gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(self));
}
static
GBINDER_LOCAL_TRANSACTION_SUPPORT
service_manager_aidl_can_handle_transaction(
GBinderLocalObject* object,
const char* iface,
guint code)
{
ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
return GBINDER_LOCAL_TRANSACTION_LOOPER;
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
can_handle_transaction(object, iface, code);
}
}
static
GBinderLocalReply*
service_manager_aidl_handle_looper_transaction(
GBinderLocalObject* object,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status)
{
if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
handle_transaction(object, req, code, flags, status);
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl_parent_class)->
handle_looper_transaction(object, req, code, flags, status);
}
}
static
void
service_manager_aidl_finalize(
GObject* object)
{
ServiceManagerAidl* self = SERVICE_MANAGER_AIDL(object);
g_hash_table_destroy(self->objects);
G_OBJECT_CLASS(service_manager_aidl_parent_class)->finalize(object);
}
static
void
service_manager_aidl_init(
ServiceManagerAidl* self)
{
self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify) gbinder_remote_object_unref);
}
static
void
service_manager_aidl_class_init(
ServiceManagerAidlClass* klass)
{
GObjectClass* object = G_OBJECT_CLASS(klass);
GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
object->finalize = service_manager_aidl_finalize;
local_object->can_handle_transaction =
service_manager_aidl_can_handle_transaction;
local_object->handle_looper_transaction =
service_manager_aidl_handle_looper_transaction;
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_add_cb(
GBinderServiceManager* sm,
int status,
void* user_data)
{
g_assert(status == GBINDER_STATUS_OK);
if (user_data) {
g_main_loop_quit(user_data);
}
}
static
void
test_get_none_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(!obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get_cb(
GBinderServiceManager* sm,
GBinderRemoteObject* obj,
int status,
void* user_data)
{
g_assert(obj);
g_assert(status == GBINDER_STATUS_OK);
g_main_loop_quit(user_data);
}
static
void
test_get()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
/* Query the object (it's not there yet) and wait for completion */
GDEBUG("Querying '%s'", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_none_cb,
loop));
test_run(&test_opt, loop);
/* Register object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, loop));
test_run(&test_opt, loop);
g_assert_cmpuint(g_hash_table_size(smsvc->objects), == ,1);
g_assert(g_hash_table_contains(smsvc->objects, name));
/* Query the object (this time it must be there) and wait for completion */
GDEBUG("Querying '%s' again", name);
g_assert(gbinder_servicemanager_get_service(sm, name, test_get_cb, loop));
test_run(&test_opt, loop);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* list
*==========================================================================*/
typedef struct test_list {
char** list;
GMainLoop* loop;
} TestList;
static
gboolean
test_list_cb(
GBinderServiceManager* sm,
char** services,
void* user_data)
{
TestList* test = user_data;
GDEBUG("Got %u name(s)", gutil_strv_length(services));
g_strfreev(test->list);
test->list = services;
g_main_loop_quit(test->loop);
return TRUE;
}
static
void
test_list()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
TestList test;
memset(&test, 0, sizeof(test));
test.loop = g_main_loop_new(NULL, FALSE);
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
/* Request the list and wait for completion */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* There's nothing there yet */
g_assert(test.list);
g_assert(!test.list[0]);
/* Register object and wait for completion */
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, test.loop));
test_run(&test_opt, test.loop);
/* Request the list again */
g_assert(gbinder_servicemanager_list(sm, test_list_cb, &test));
test_run(&test_opt, test.loop);
/* Now the name must be there */
g_assert_cmpuint(gutil_strv_length(test.list), == ,1);
g_assert_cmpstr(test.list[0], == ,name);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_strfreev(test.list);
g_main_loop_unref(test.loop);
}
/*==========================================================================*
* notify
*==========================================================================*/
static
void
test_notify_cb(
GBinderServiceManager* sm,
const char* name,
void* user_data)
{
g_assert(name);
GDEBUG("'%s' is registered", name);
g_main_loop_quit(user_data);
}
static
void
test_notify()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* svc = servicemanager_aidl_new(other_dev, FALSE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
const char* name = "name";
GBinderServiceManager* sm;
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id;
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
gbinder_ipc_set_max_threads(ipc, 1);
/* Start watching */
id = gbinder_servicemanager_add_registration_handler(sm, name,
test_notify_cb, loop);
g_assert(id);
/* Register the object and wait for completion */
GDEBUG("Registering object '%s' => %p", name, obj);
g_assert(gbinder_servicemanager_add_service(sm, name, obj,
test_add_cb, NULL));
/* test_notify_cb will stop the loop */
test_run(&test_opt, loop);
gbinder_servicemanager_remove_handler(sm, id);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(svc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* notify2
*==========================================================================*/
static
void
test_notify2()
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
GBinderIpc* ipc = gbinder_ipc_new(dev);
ServiceManagerAidl* smsvc = servicemanager_aidl_new(other_dev, TRUE);
GBinderLocalObject* obj = gbinder_local_object_new(ipc, NULL, NULL, NULL);
const int fd = gbinder_driver_fd(ipc->driver);
GBinderServiceManager* sm;
const char* name1 = "name1";
const char* name2 = "name2";
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
gulong id1, id2;
/* Set up binder simulator */
test_binder_register_object(fd, obj, AUTO_HANDLE);
test_binder_set_passthrough(fd, TRUE);
sm = gbinder_servicemanager_new(dev);
gbinder_ipc_set_max_threads(ipc, 1);
/* Register the object synchronously (twice)*/
GDEBUG("Registering object '%s' => %p", name1, obj);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name1,obj),==,0);
g_assert(gbinder_servicemanager_get_service_sync(sm, name1, NULL));
GDEBUG("Registering object '%s' => %p", name2, obj);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(sm,name2,obj),==,0);
g_assert(gbinder_servicemanager_get_service_sync(sm, name2, NULL));
/* Watch for the first name to create internal name watcher */
id1 = gbinder_servicemanager_add_registration_handler(sm, name1,
test_notify_cb, loop);
g_assert(id1);
test_run(&test_opt, loop);
/* Now watch for the second name */
id2 = gbinder_servicemanager_add_registration_handler(sm, name2,
test_notify_cb, loop);
g_assert(id2);
test_run(&test_opt, loop);
gbinder_servicemanager_remove_handler(sm, id1);
gbinder_servicemanager_remove_handler(sm, id2);
test_binder_unregister_objects(fd);
gbinder_local_object_unref(obj);
servicemanager_aidl_free(smsvc);
gbinder_servicemanager_unref(sm);
gbinder_ipc_unref(ipc);
gbinder_ipc_exit();
test_binder_exit_wait();
g_main_loop_unref(loop);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager_aidl/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("list"), test_list);
g_test_add_func(TEST_("notify"), test_notify);
g_test_add_func(TEST_("notify2"), test_notify2);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -0,0 +1,5 @@
# -*- Mode: makefile-gmake -*-
EXE = unit_servicemanager_aidl2
include ../common/Makefile

View File

@@ -0,0 +1,437 @@
/*
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "test_binder.h"
#include "gbinder_driver.h"
#include "gbinder_config.h"
#include "gbinder_ipc.h"
#include "gbinder_reader.h"
#include "gbinder_servicemanager_p.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_local_object_p.h"
#include "gbinder_local_reply.h"
#include "gbinder_remote_request.h"
#include "gbinder_remote_object.h"
#include <gutil_strv.h>
#include <gutil_log.h>
static TestOpt test_opt;
static const char TMP_DIR_TEMPLATE[] =
"gbinder-test-servicemanager_aidl2-XXXXXX";
GType
gbinder_servicemanager_hidl_get_type()
{
/* Avoid pulling in gbinder_servicemanager_hidl object */
return 0;
}
/*==========================================================================*
* Test service manager
*==========================================================================*/
#define SVCMGR_HANDLE (0)
static const char SVCMGR_IFACE[] = "android.os.IServiceManager";
enum servicemanager_aidl_tx {
GET_SERVICE_TRANSACTION = GBINDER_FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION
};
const char* const servicemanager_aidl_ifaces[] = { SVCMGR_IFACE, NULL };
typedef GBinderLocalObjectClass ServiceManagerAidl2Class;
typedef struct service_manager_aidl2 {
GBinderLocalObject parent;
GHashTable* objects;
gboolean handle_on_looper_thread;
} ServiceManagerAidl2;
#define SERVICE_MANAGER_AIDL2_TYPE (service_manager_aidl2_get_type())
#define SERVICE_MANAGER_AIDL2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
SERVICE_MANAGER_AIDL2_TYPE, ServiceManagerAidl2))
G_DEFINE_TYPE(ServiceManagerAidl2, service_manager_aidl2, \
GBINDER_TYPE_LOCAL_OBJECT)
static
GBinderLocalReply*
servicemanager_aidl2_handler(
GBinderLocalObject* obj,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status,
void* user_data)
{
ServiceManagerAidl2* self = user_data;
GBinderLocalReply* reply = NULL;
GBinderReader reader;
GBinderRemoteObject* remote_obj;
guint32 num, allow_isolated, dumpsys_priority;
char* str;
g_assert(!flags);
GDEBUG("%s %u", gbinder_remote_request_interface(req), code);
g_assert_cmpstr(gbinder_remote_request_interface(req), == ,SVCMGR_IFACE);
*status = -1;
switch (code) {
case GET_SERVICE_TRANSACTION:
case CHECK_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
if (str) {
reply = gbinder_local_object_new_reply(obj);
remote_obj = g_hash_table_lookup(self->objects, str);
if (remote_obj) {
GDEBUG("Found name '%s' => %p", str, remote_obj);
gbinder_local_reply_append_remote_object(reply, remote_obj);
} else {
GDEBUG("Name '%s' not found", str);
gbinder_local_reply_append_int32(reply, GBINDER_STATUS_OK);
}
g_free(str);
}
break;
case ADD_SERVICE_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
str = gbinder_reader_read_string16(&reader);
remote_obj = gbinder_reader_read_object(&reader);
if (str && remote_obj &&
gbinder_reader_read_uint32(&reader, &allow_isolated) &&
gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
GDEBUG("Adding '%s'", str);
g_hash_table_replace(self->objects, str, remote_obj);
remote_obj = NULL;
str = NULL;
reply = gbinder_local_object_new_reply(obj);
*status = GBINDER_STATUS_OK;
}
g_free(str);
gbinder_remote_object_unref(remote_obj);
break;
case LIST_SERVICES_TRANSACTION:
gbinder_remote_request_init_reader(req, &reader);
if (gbinder_reader_read_uint32(&reader, &num) &&
gbinder_reader_read_uint32(&reader, &dumpsys_priority)) {
if (num < g_hash_table_size(self->objects)) {
GList* keys = g_hash_table_get_keys(self->objects);
GList* l = g_list_nth(keys, num);
/* Ignore dumpsys_priority */
reply = gbinder_local_object_new_reply(obj);
gbinder_local_reply_append_string16(reply, l->data);
g_list_free(keys);
*status = GBINDER_STATUS_OK;
} else {
GDEBUG("Index %u out of bounds", num);
}
}
break;
default:
GDEBUG("Unhandled command %u", code);
break;
}
return reply;
}
static
ServiceManagerAidl2*
servicemanager_aidl2_new(
const char* dev,
gboolean handle_on_looper_thread)
{
ServiceManagerAidl2* self = g_object_new(SERVICE_MANAGER_AIDL2_TYPE, NULL);
GBinderLocalObject* obj = GBINDER_LOCAL_OBJECT(self);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
self->handle_on_looper_thread = handle_on_looper_thread;
gbinder_local_object_init_base(obj, ipc, servicemanager_aidl_ifaces,
servicemanager_aidl2_handler, self);
test_binder_set_looper_enabled(fd, TRUE);
test_binder_register_object(fd, obj, SVCMGR_HANDLE);
gbinder_ipc_register_local_object(ipc, obj);
gbinder_ipc_unref(ipc);
return self;
}
static
GBINDER_LOCAL_TRANSACTION_SUPPORT
service_manager_aidl2_can_handle_transaction(
GBinderLocalObject* object,
const char* iface,
guint code)
{
ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
if (self->handle_on_looper_thread && !g_strcmp0(SVCMGR_IFACE, iface)) {
return GBINDER_LOCAL_TRANSACTION_LOOPER;
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
can_handle_transaction(object, iface, code);
}
}
static
GBinderLocalReply*
service_manager_aidl2_handle_looper_transaction(
GBinderLocalObject* object,
GBinderRemoteRequest* req,
guint code,
guint flags,
int* status)
{
if (!g_strcmp0(gbinder_remote_request_interface(req), SVCMGR_IFACE)) {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
handle_transaction(object, req, code, flags, status);
} else {
return GBINDER_LOCAL_OBJECT_CLASS(service_manager_aidl2_parent_class)->
handle_looper_transaction(object, req, code, flags, status);
}
}
static
void
service_manager_aidl2_finalize(
GObject* object)
{
ServiceManagerAidl2* self = SERVICE_MANAGER_AIDL2(object);
g_hash_table_destroy(self->objects);
G_OBJECT_CLASS(service_manager_aidl2_parent_class)->finalize(object);
}
static
void
service_manager_aidl2_init(
ServiceManagerAidl2* self)
{
self->objects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify) gbinder_remote_object_unref);
}
static
void
service_manager_aidl2_class_init(
ServiceManagerAidl2Class* klass)
{
GObjectClass* object = G_OBJECT_CLASS(klass);
GBinderLocalObjectClass* local_object = GBINDER_LOCAL_OBJECT_CLASS(klass);
object->finalize = service_manager_aidl2_finalize;
local_object->can_handle_transaction =
service_manager_aidl2_can_handle_transaction;
local_object->handle_looper_transaction =
service_manager_aidl2_handle_looper_transaction;
}
/*==========================================================================*
* Test context
*==========================================================================*/
typedef struct test_context {
const char* default_config_dir;
const char* default_config_file;
char* config_dir;
char* config_subdir;
char* config_file;
GBinderLocalObject* object;
ServiceManagerAidl2* service;
GBinderServiceManager* client;
int fd;
} TestContext;
static
void
test_context_init(
TestContext* test)
{
const char* dev = GBINDER_DEFAULT_BINDER;
const char* other_dev = GBINDER_DEFAULT_BINDER "-private";
/*
* Also set defaults so that both /dev/binder and /dev/binder-private
* use the same protocol.
*/
const char* config =
"[Protocol]\n"
"Default = aidl2\n"
"/dev/binder = aidl2\n"
"[ServiceManager]\n"
"Default = aidl2\n"
"/dev/binder = aidl2\n";
GBinderIpc* ipc;
memset(test, 0, sizeof(*test));
test->default_config_dir = gbinder_config_dir;
test->default_config_file = gbinder_config_file;
test->config_dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
test->config_subdir = g_build_filename(test->config_dir, "d", NULL);
test->config_file = g_build_filename(test->config_dir, "test.conf", NULL);
g_assert(g_file_set_contents(test->config_file, config, -1, NULL));
GDEBUG("Config file %s", test->config_file);
gbinder_config_dir = test->config_subdir; /* Doesn't exist */
gbinder_config_file = test->config_file;
ipc = gbinder_ipc_new(dev);
test->fd = gbinder_driver_fd(ipc->driver);
test->object = gbinder_local_object_new(ipc, NULL, NULL, NULL);
/* Set up binder simulator */
test_binder_register_object(test->fd, test->object, AUTO_HANDLE);
test_binder_set_passthrough(test->fd, TRUE);
test->service = servicemanager_aidl2_new(other_dev, TRUE);
test->client = gbinder_servicemanager_new(dev);
gbinder_ipc_unref(ipc);
}
static
void
test_context_deinit(
TestContext* test)
{
test_binder_unregister_objects(test->fd);
gbinder_local_object_unref(test->object);
gbinder_local_object_drop(GBINDER_LOCAL_OBJECT(test->service));
gbinder_servicemanager_unref(test->client);
gbinder_ipc_exit();
test_binder_exit_wait();
remove(test->config_file);
remove(test->config_dir);
g_free(test->config_file);
g_free(test->config_subdir);
g_free(test->config_dir);
gbinder_config_dir = test->default_config_dir;
gbinder_config_file = test->default_config_file;
gbinder_config_exit();
}
/*==========================================================================*
* get
*==========================================================================*/
static
void
test_get()
{
TestContext test;
const char* name = "name";
int status = -1;
test_context_init(&test);
/* Query the object (it's not there yet) */
GDEBUG("Querying '%s'", name);
g_assert(!gbinder_servicemanager_get_service_sync(test.client,
name, &status));
g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
/* Register object */
GDEBUG("Registering object '%s' => %p", name, test.object);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
name, test.object), == ,GBINDER_STATUS_OK);
g_assert_cmpuint(g_hash_table_size(test.service->objects), == ,1);
g_assert(g_hash_table_contains(test.service->objects, name));
/* Query the object (this time it must be there) */
GDEBUG("Querying '%s' again", name);
g_assert(gbinder_servicemanager_get_service_sync(test.client, name,
&status));
g_assert_cmpint(status, == ,GBINDER_STATUS_OK);
test_context_deinit(&test);
}
/*==========================================================================*
* list
*==========================================================================*/
static
void
test_list()
{
TestContext test;
const char* name = "name";
char** list;
test_context_init(&test);
/* Request the list */
list = gbinder_servicemanager_list_sync(test.client);
/* There's nothing there yet */
g_assert(list);
g_assert(!list[0]);
g_strfreev(list);
/* Register object */
GDEBUG("Registering object '%s' => %p", name, test.object);
g_assert_cmpint(gbinder_servicemanager_add_service_sync(test.client,
name, test.object), == ,GBINDER_STATUS_OK);
/* Request the list again */
list = gbinder_servicemanager_list_sync(test.client);
/* Now the name must be there */
g_assert_cmpuint(gutil_strv_length(list), == ,1);
g_assert_cmpstr(list[0], == ,name);
g_strfreev(list);
test_context_deinit(&test);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_(t) "/servicemanager_aidl2/" t
int main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_("get"), test_get);
g_test_add_func(TEST_("list"), test_list);
test_init(&test_opt, argc, argv);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2019-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -202,7 +202,6 @@ test_servicemanager_class_init(
{
klass->iface = TEST_SERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
@@ -212,18 +211,24 @@ test_servicemanager_class_init(
G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
}
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
/* Avoid pulling in the actual objects */
GType
gbinder_servicemanager_aidl_get_type()
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
return TEST_TYPE_SERVICEMANAGER;
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
GType
gbinder_servicemanager_aidl2_get_type()
{
return gbinder_servicemanager_new(dev);
return TEST_TYPE_SERVICEMANAGER;
}
GType
gbinder_servicemanager_hidl_get_type()
{
return TEST_TYPE_SERVICEMANAGER;
}
/*==========================================================================*
@@ -236,7 +241,7 @@ test_null(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServiceManager* sm;
test_setup_ping(ipc);
@@ -263,7 +268,7 @@ test_basic(
const char* obj_name = "test";
const char* dev = GBINDER_DEFAULT_BINDER;
const char* const ifaces[] = { "interface", NULL };
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj;
GBinderServiceManager* sm;
@@ -306,7 +311,7 @@ test_present(
const char* obj_name = "test";
const char* const ifaces[] = { "interface", NULL };
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj;
@@ -340,6 +345,7 @@ test_present(
gbinder_ipc_unref(ipc);
test_run(&test_opt, loop);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}
@@ -371,7 +377,7 @@ test_not_present(
const char* obj_name = "test";
const char* const ifaces[] = { "interface", NULL };
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj;
@@ -421,7 +427,7 @@ test_cancel(
const char* obj_name = "test";
const char* const ifaces[] = { "interface", NULL };
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
const int fd = gbinder_driver_fd(ipc->driver);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderLocalObject* obj;
@@ -469,6 +475,7 @@ test_cancel(
gbinder_ipc_unref(ipc);
test_run(&test_opt, loop);
gbinder_ipc_exit();
g_main_loop_unref(loop);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -174,7 +174,6 @@ test_servicemanager_class_init(
{
klass->iface = TEST_SERVICEMANAGER_IFACE;
klass->default_device = GBINDER_DEFAULT_HWBINDER;
klass->rpc_protocol = &gbinder_rpc_protocol_binder;
klass->list = test_servicemanager_list;
klass->get_service = test_servicemanager_get_service;
klass->add_service = test_servicemanager_add_service;
@@ -184,18 +183,24 @@ test_servicemanager_class_init(
G_OBJECT_CLASS(klass)->finalize = test_servicemanager_finalize;
}
GBinderServiceManager*
gbinder_defaultservicemanager_new(
const char* dev)
/* Avoid pulling in the actual objects */
GType
gbinder_servicemanager_aidl_get_type()
{
return gbinder_servicemanager_new_with_type(TEST_TYPE_SERVICEMANAGER, dev);
return TEST_TYPE_SERVICEMANAGER;
}
GBinderServiceManager*
gbinder_hwservicemanager_new(
const char* dev)
GType
gbinder_servicemanager_aidl2_get_type()
{
return gbinder_servicemanager_new(dev);
return TEST_TYPE_SERVICEMANAGER;
}
GType
gbinder_servicemanager_hidl_get_type()
{
return TEST_TYPE_SERVICEMANAGER;
}
/*==========================================================================*
@@ -225,7 +230,7 @@ test_basic(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager;
GBinderServicePoll* poll;
@@ -301,7 +306,7 @@ test_notify1(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager;
@@ -375,7 +380,7 @@ test_notify2(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager;
@@ -434,7 +439,7 @@ test_already_there(
void)
{
const char* dev = GBINDER_DEFAULT_BINDER;
GBinderIpc* ipc = gbinder_ipc_new(dev, NULL);
GBinderIpc* ipc = gbinder_ipc_new(dev);
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
GBinderServicePoll* weakptr = NULL;
GBinderServiceManager* manager;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2018-2019 Slava Monich <slava.monich@jolla.com>
* Copyright (C) 2018-2020 Jolla Ltd.
* Copyright (C) 2018-2020 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of BSD license as follows:
*
@@ -14,8 +14,8 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -485,8 +485,6 @@ typedef struct test_hidl_vec_data {
guint buffers_size;
} TestHidlVecData;
static guint test_hidl_vec_offsets_0[] =
{ 0 };
static guint test_hidl_vec_offsets_32[] =
{ 0, BUFFER_OBJECT_SIZE_32 };
static guint test_hidl_vec_offsets_64[] =
@@ -494,12 +492,12 @@ static guint test_hidl_vec_offsets_64[] =
static const TestHidlVecData test_hidl_vec_tests[] = {
{ "32/null", &gbinder_io_32, NULL, 0, 0,
TEST_ARRAY_AND_COUNT(test_hidl_vec_offsets_0), sizeof(GBinderHidlVec) },
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_0), sizeof(GBinderHidlVec) },
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 */ }
@@ -544,8 +542,6 @@ typedef struct test_hidl_string_data {
guint buffers_size;
} TestHidlStringData;
static guint test_hidl_string_offsets_0[] =
{ 0 };
static guint test_hidl_string_offsets_32[] =
{ 0, BUFFER_OBJECT_SIZE_32 };
static guint test_hidl_string_offsets_64[] =
@@ -553,13 +549,13 @@ static guint test_hidl_string_offsets_64[] =
static const TestHidlStringData test_hidl_string_tests[] = {
{ "32/null", &gbinder_io_32, NULL,
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_0),
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_32),
sizeof(GBinderHidlString) },
{ "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_0),
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
sizeof(GBinderHidlString) },
{ "64/xxxxxxx", &gbinder_io_64, "xxxxxxx",
TEST_ARRAY_AND_COUNT(test_hidl_string_offsets_64),
@@ -607,7 +603,7 @@ test_hidl_string2(
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
g_assert(offsets->count == 3);
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);
@@ -634,8 +630,10 @@ typedef struct test_hidl_string_vec_data {
static char* test_hidl_string_vec_data_1[] = { "test" };
static guint test_hidl_string_vec_offsets_empty[] =
{ 0 };
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[] =
@@ -643,14 +641,14 @@ static guint test_hidl_string_vec_offsets_1_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),
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),
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),