fix: Add build script and fix manifest

- Add build-apk.sh for easy compilation
- Simplify AndroidManifest.xml
- Fix themes.xml to use basic Android themes
- Enable BuildConfig
- Add okhttp-dnsoverhttps dependency
This commit is contained in:
Renato
2026-01-28 22:36:38 +00:00
parent d3ce7872e7
commit 2ccdf3a78c
11853 changed files with 741631 additions and 543 deletions

View File

@@ -0,0 +1,403 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_auto_utils.h
* @brief These objects provide a more C++-like thin interface to the binder.
*/
#pragma once
#include <android/binder_ibinder.h>
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
#include <assert.h>
#include <unistd.h>
#include <cstddef>
#include <iostream>
#include <string>
namespace ndk {
/**
* Represents one strong pointer to an AIBinder object.
*/
class SpAIBinder {
public:
/**
* Default constructor.
*/
SpAIBinder() : mBinder(nullptr) {}
/**
* Takes ownership of one strong refcount of binder.
*/
explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
/**
* Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not
* explicit because it is not taking ownership of anything.
*/
SpAIBinder(std::nullptr_t) : SpAIBinder() {} // NOLINT(google-explicit-constructor)
/**
* This will delete the underlying object if it exists. See operator=.
*/
SpAIBinder(const SpAIBinder& other) { *this = other; }
/**
* This deletes the underlying object if it exists. See set.
*/
~SpAIBinder() { set(nullptr); }
/**
* This takes ownership of a binder from another AIBinder object but it does not affect the
* ownership of that other object.
*/
SpAIBinder& operator=(const SpAIBinder& other) {
if (this == &other) {
return *this;
}
AIBinder_incStrong(other.mBinder);
set(other.mBinder);
return *this;
}
/**
* Takes ownership of one strong refcount of binder
*/
void set(AIBinder* binder) {
AIBinder* old = *const_cast<AIBinder* volatile*>(&mBinder);
if (old != nullptr) AIBinder_decStrong(old);
if (old != *const_cast<AIBinder* volatile*>(&mBinder)) {
__assert(__FILE__, __LINE__, "Race detected.");
}
mBinder = binder;
}
/**
* This returns the underlying binder object for transactions. If it is used to create another
* SpAIBinder object, it should first be incremented.
*/
AIBinder* get() const { return mBinder; }
/**
* This allows the value in this class to be set from beneath it. If you call this method and
* then change the value of T*, you must take ownership of the value you are replacing and add
* ownership to the object that is put in here.
*
* Recommended use is like this:
* SpAIBinder a; // will be nullptr
* SomeInitFunction(a.getR()); // value is initialized with refcount
*
* Other usecases are discouraged.
*
*/
AIBinder** getR() { return &mBinder; }
bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); }
bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); }
bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); }
bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); }
bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); }
bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); }
private:
AIBinder* mBinder = nullptr;
};
namespace impl {
/**
* This baseclass owns a single object, used to make various classes RAII.
*/
template <typename T, void (*Destroy)(T), T DEFAULT>
class ScopedAResource {
public:
/**
* Takes ownership of t.
*/
explicit ScopedAResource(T t = DEFAULT) : mT(t) {}
/**
* This deletes the underlying object if it exists. See set.
*/
~ScopedAResource() { set(DEFAULT); }
/**
* Takes ownership of t.
*/
void set(T t) {
Destroy(mT);
mT = t;
}
/**
* This returns the underlying object to be modified but does not affect ownership.
*/
T get() { return mT; }
/**
* This returns the const underlying object but does not affect ownership.
*/
const T get() const { return mT; }
/**
* Release the underlying resource.
*/
[[nodiscard]] T release() {
T a = mT;
mT = DEFAULT;
return a;
}
/**
* This allows the value in this class to be set from beneath it. If you call this method and
* then change the value of T*, you must take ownership of the value you are replacing and add
* ownership to the object that is put in here.
*
* Recommended use is like this:
* ScopedAResource<T> a; // will be nullptr
* SomeInitFunction(a.getR()); // value is initialized with refcount
*
* Other usecases are discouraged.
*
*/
T* getR() { return &mT; }
// copy-constructing/assignment is disallowed
ScopedAResource(const ScopedAResource&) = delete;
ScopedAResource& operator=(const ScopedAResource&) = delete;
// move-constructing/assignment is okay
ScopedAResource(ScopedAResource&& other) noexcept : mT(std::move(other.mT)) {
other.mT = DEFAULT;
}
ScopedAResource& operator=(ScopedAResource&& other) noexcept {
set(other.mT);
other.mT = DEFAULT;
return *this;
}
private:
T mT;
};
} // namespace impl
/**
* Convenience wrapper. See AParcel.
*/
class ScopedAParcel : public impl::ScopedAResource<AParcel*, AParcel_delete, nullptr> {
public:
/**
* Takes ownership of a.
*/
explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {}
~ScopedAParcel() {}
ScopedAParcel(ScopedAParcel&&) = default;
ScopedAParcel& operator=(ScopedAParcel&&) = default;
bool operator!=(const ScopedAParcel& rhs) const { return get() != rhs.get(); }
bool operator<(const ScopedAParcel& rhs) const { return get() < rhs.get(); }
bool operator<=(const ScopedAParcel& rhs) const { return get() <= rhs.get(); }
bool operator==(const ScopedAParcel& rhs) const { return get() == rhs.get(); }
bool operator>(const ScopedAParcel& rhs) const { return get() > rhs.get(); }
bool operator>=(const ScopedAParcel& rhs) const { return get() >= rhs.get(); }
};
/**
* Convenience wrapper. See AStatus.
*/
class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nullptr> {
public:
/**
* Takes ownership of a.
*
* WARNING: this constructor is only expected to be used when reading a
* status value. Use `ScopedAStatus::ok()` instead.
*/
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
ScopedAStatus(ScopedAStatus&&) = default;
ScopedAStatus& operator=(ScopedAStatus&&) = default;
/**
* See AStatus_isOk.
*/
bool isOk() const { return get() != nullptr && AStatus_isOk(get()); }
/**
* See AStatus_getExceptionCode
*/
binder_exception_t getExceptionCode() const { return AStatus_getExceptionCode(get()); }
/**
* See AStatus_getServiceSpecificError
*/
int32_t getServiceSpecificError() const { return AStatus_getServiceSpecificError(get()); }
/**
* See AStatus_getStatus
*/
binder_status_t getStatus() const { return AStatus_getStatus(get()); }
/**
* See AStatus_getMessage
*/
const char* getMessage() const { return AStatus_getMessage(get()); }
std::string getDescription() const {
#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
#endif
#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 30
const char* cStr = AStatus_getDescription(get());
std::string ret = cStr;
AStatus_deleteDescription(cStr);
return ret;
#endif
#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
}
#endif
binder_exception_t exception = getExceptionCode();
std::string desc = std::to_string(exception);
if (exception == EX_SERVICE_SPECIFIC) {
desc += " (" + std::to_string(getServiceSpecificError()) + ")";
} else if (exception == EX_TRANSACTION_FAILED) {
desc += " (" + std::to_string(getStatus()) + ")";
}
if (const char* msg = getMessage(); msg != nullptr) {
desc += ": ";
desc += msg;
}
return desc;
}
/**
* Convenience methods for creating scoped statuses.
*/
static ScopedAStatus ok() { return ScopedAStatus(AStatus_newOk()); }
static ScopedAStatus fromExceptionCode(binder_exception_t exception) {
return ScopedAStatus(AStatus_fromExceptionCode(exception));
}
static ScopedAStatus fromExceptionCodeWithMessage(binder_exception_t exception,
const char* message) {
return ScopedAStatus(AStatus_fromExceptionCodeWithMessage(exception, message));
}
static ScopedAStatus fromServiceSpecificError(int32_t serviceSpecific) {
return ScopedAStatus(AStatus_fromServiceSpecificError(serviceSpecific));
}
static ScopedAStatus fromServiceSpecificErrorWithMessage(int32_t serviceSpecific,
const char* message) {
return ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(serviceSpecific, message));
}
static ScopedAStatus fromStatus(binder_status_t status) {
return ScopedAStatus(AStatus_fromStatus(status));
}
};
static inline std::ostream& operator<<(std::ostream& os, const ScopedAStatus& status) {
return os << status.getDescription();
return os;
}
/**
* Convenience wrapper. See AIBinder_DeathRecipient.
*/
class ScopedAIBinder_DeathRecipient
: public impl::ScopedAResource<AIBinder_DeathRecipient*, AIBinder_DeathRecipient_delete,
nullptr> {
public:
/**
* Takes ownership of a.
*/
explicit ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient* a = nullptr)
: ScopedAResource(a) {}
~ScopedAIBinder_DeathRecipient() {}
ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default;
ScopedAIBinder_DeathRecipient& operator=(ScopedAIBinder_DeathRecipient&&) = default;
};
/**
* Convenience wrapper. See AIBinder_Weak.
*/
class ScopedAIBinder_Weak
: public impl::ScopedAResource<AIBinder_Weak*, AIBinder_Weak_delete, nullptr> {
public:
/**
* Takes ownership of a.
*/
explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {}
~ScopedAIBinder_Weak() {}
ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default;
ScopedAIBinder_Weak& operator=(ScopedAIBinder_Weak&&) = default;
/**
* See AIBinder_Weak_promote.
*/
SpAIBinder promote() const { return SpAIBinder(AIBinder_Weak_promote(get())); }
};
namespace internal {
static void closeWithError(int fd) {
if (fd == -1) return;
int ret = close(fd);
if (ret != 0) {
syslog(LOG_ERR, "Could not close FD %d: %s", fd, strerror(errno));
}
}
} // namespace internal
/**
* Convenience wrapper for a file descriptor.
*/
class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWithError, -1> {
public:
/**
* Takes ownership of a.
*/
ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); }
bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); }
bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); }
bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); }
bool operator==(const ScopedFileDescriptor& rhs) const { return get() == rhs.get(); }
bool operator>(const ScopedFileDescriptor& rhs) const { return get() > rhs.get(); }
bool operator>=(const ScopedFileDescriptor& rhs) const { return get() >= rhs.get(); }
};
} // namespace ndk
/** @} */

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_enums.h
* @brief Helpers for AIDL enum types.
*/
#pragma once
#include <iterator>
#include <type_traits>
namespace ndk {
namespace internal {
/**
* Never instantiated. Used as a placeholder for template variables.
*/
template <typename T>
struct invalid_type;
/**
* AIDL generates specializations of this for enums.
*/
template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
constexpr invalid_type<EnumType> enum_values;
} // namespace internal
/**
* Iterable interface to enumerate all values of AIDL enum types.
*/
template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
struct enum_range {
/**
* Return an iterator pointing to the first enum value.
*/
constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
/**
* Return an iterator pointing to one past the last enum value.
*/
constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
};
} // namespace ndk
/** @} */

View File

@@ -0,0 +1,377 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_interface_utils.h
* @brief This provides common C++ classes for common operations and as base classes for C++
* interfaces.
*/
#pragma once
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
#define HAS_BINDER_SHELL_COMMAND
#endif //_has_include
#include <assert.h>
#include <memory>
#include <mutex>
namespace ndk {
/**
* Binder analog to using std::shared_ptr for an internally held refcount.
*
* ref must be called at least one time during the lifetime of this object. The recommended way to
* construct this object is with SharedRefBase::make.
*
* If you need a "this" shared reference analogous to shared_from_this, use this->ref().
*/
class SharedRefBase {
public:
SharedRefBase() {}
virtual ~SharedRefBase() {
std::call_once(mFlagThis, [&]() {
__assert(__FILE__, __LINE__, "SharedRefBase: no ref created during lifetime");
});
if (ref() != nullptr) {
__assert(__FILE__, __LINE__,
"SharedRefBase: destructed but still able to lock weak_ptr. Is this object "
"double-owned?");
}
}
/**
* A shared_ptr must be held to this object when this is called. This must be called once during
* the lifetime of this object.
*/
std::shared_ptr<SharedRefBase> ref() {
std::shared_ptr<SharedRefBase> thiz = mThis.lock();
std::call_once(mFlagThis, [&]() { mThis = thiz = std::shared_ptr<SharedRefBase>(this); });
return thiz;
}
/**
* Convenience method for a ref (see above) which automatically casts to the desired child type.
*/
template <typename CHILD>
std::shared_ptr<CHILD> ref() {
return std::static_pointer_cast<CHILD>(ref());
}
/**
* Convenience method for making an object directly with a reference.
*/
template <class T, class... Args>
static std::shared_ptr<T> make(Args&&... args) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
T* t = new T(std::forward<Args>(args)...);
#pragma clang diagnostic pop
// warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
return t->template ref<T>(); // NOLINT(clang-analyzer-unix.Malloc)
}
static void operator delete(void* p) { std::free(p); }
// Once minSdkVersion is 30, we are guaranteed to be building with the
// Android 11 AIDL compiler which supports the SharedRefBase::make API.
//
// Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
// ownership. Making this operator private to avoid double-ownership.
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
private:
#else
[[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
#endif
static void* operator new(size_t s) { return std::malloc(s); }
private:
std::once_flag mFlagThis;
std::weak_ptr<SharedRefBase> mThis;
};
/**
* wrapper analog to IInterface
*/
class ICInterface : public SharedRefBase {
public:
ICInterface() {}
virtual ~ICInterface() {}
/**
* This either returns the single existing implementation or creates a new implementation.
*/
virtual SpAIBinder asBinder() = 0;
/**
* Returns whether this interface is in a remote process. If it cannot be determined locally,
* this will be checked using AIBinder_isRemote.
*/
virtual bool isRemote() = 0;
/**
* Dumps information about the interface. By default, dumps nothing.
*/
virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
#ifdef HAS_BINDER_SHELL_COMMAND
/**
* Process shell commands. By default, does nothing.
*/
virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv,
uint32_t argc);
#endif
/**
* Interprets this binder as this underlying interface if this has stored an ICInterface in the
* binder's user data.
*
* This does not do type checking and should only be used when the binder is known to originate
* from ICInterface. Most likely, you want to use I*::fromBinder.
*/
static inline std::shared_ptr<ICInterface> asInterface(AIBinder* binder);
/**
* Helper method to create a class
*/
static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact);
private:
class ICInterfaceData {
public:
std::shared_ptr<ICInterface> interface;
static inline std::shared_ptr<ICInterface> getInterface(AIBinder* binder);
static inline void* onCreate(void* args);
static inline void onDestroy(void* userData);
static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs);
#ifdef HAS_BINDER_SHELL_COMMAND
static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err,
const char** argv, uint32_t argc);
#endif
};
};
/**
* implementation of IInterface for server (n = native)
*/
template <typename INTERFACE>
class BnCInterface : public INTERFACE {
public:
BnCInterface() {}
virtual ~BnCInterface() {}
SpAIBinder asBinder() override final;
bool isRemote() override final { return false; }
static std::string makeServiceName(std::string_view instance) {
return INTERFACE::descriptor + ("/" + std::string(instance));
}
protected:
/**
* This function should only be called by asBinder. Otherwise, there is a possibility of
* multiple AIBinder* objects being created for the same instance of an object.
*/
virtual SpAIBinder createBinder() = 0;
private:
std::mutex mMutex; // for asBinder
ScopedAIBinder_Weak mWeakBinder;
};
/**
* implementation of IInterface for client (p = proxy)
*/
template <typename INTERFACE>
class BpCInterface : public INTERFACE {
public:
explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {}
virtual ~BpCInterface() {}
SpAIBinder asBinder() override final;
bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); }
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
return AIBinder_dump(asBinder().get(), fd, args, numArgs);
}
private:
SpAIBinder mBinder;
};
// END OF CLASS DECLARATIONS
binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) {
return STATUS_OK;
}
#ifdef HAS_BINDER_SHELL_COMMAND
binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/,
const char** /*argv*/, uint32_t /*argc*/) {
return STATUS_OK;
}
#endif
std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
return ICInterfaceData::getInterface(binder);
}
AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact) {
AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate,
ICInterfaceData::onDestroy, onTransact);
if (clazz == nullptr) {
return nullptr;
}
// We can't know if these methods are overridden by a subclass interface, so we must register
// ourselves. The defaults are harmless.
AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
#ifdef HAS_BINDER_SHELL_COMMAND
#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
#else
if (__ANDROID_API__ >= 30) {
#endif
AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
}
#endif
return clazz;
}
std::shared_ptr<ICInterface> ICInterface::ICInterfaceData::getInterface(AIBinder* binder) {
if (binder == nullptr) return nullptr;
void* userData = AIBinder_getUserData(binder);
if (userData == nullptr) return nullptr;
return static_cast<ICInterfaceData*>(userData)->interface;
}
void* ICInterface::ICInterfaceData::onCreate(void* args) {
std::shared_ptr<ICInterface> interface = static_cast<ICInterface*>(args)->ref<ICInterface>();
ICInterfaceData* data = new ICInterfaceData{interface};
return static_cast<void*>(data);
}
void ICInterface::ICInterfaceData::onDestroy(void* userData) {
delete static_cast<ICInterfaceData*>(userData);
}
binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
if (interface != nullptr) {
return interface->dump(fd, args, numArgs);
}
return STATUS_DEAD_OBJECT;
}
#ifdef HAS_BINDER_SHELL_COMMAND
binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out,
int err, const char** argv,
uint32_t argc) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
if (interface != nullptr) {
return interface->handleShellCommand(in, out, err, argv, argc);
}
return STATUS_DEAD_OBJECT;
}
#endif
template <typename INTERFACE>
SpAIBinder BnCInterface<INTERFACE>::asBinder() {
std::lock_guard<std::mutex> l(mMutex);
SpAIBinder binder;
if (mWeakBinder.get() != nullptr) {
binder.set(AIBinder_Weak_promote(mWeakBinder.get()));
}
if (binder.get() == nullptr) {
binder = createBinder();
mWeakBinder.set(AIBinder_Weak_new(binder.get()));
}
return binder;
}
template <typename INTERFACE>
SpAIBinder BpCInterface<INTERFACE>::asBinder() {
return mBinder;
}
} // namespace ndk
// Once minSdkVersion is 30, we are guaranteed to be building with the
// Android 11 AIDL compiler which supports the SharedRefBase::make API.
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
namespace ndk::internal {
template <typename T, typename = void>
struct is_complete_type : std::false_type {};
template <typename T>
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
} // namespace ndk::internal
namespace std {
// Define `SharedRefBase` specific versions of `std::make_shared` and
// `std::make_unique` to block people from using them. Using them to allocate
// `ndk::SharedRefBase` objects results in double ownership. Use
// `ndk::SharedRefBase::make<T>(...)` instead.
//
// Note: We exclude incomplete types because `std::is_base_of` is undefined in
// that case.
template <typename T, typename... Args,
std::enable_if_t<ndk::internal::is_complete_type<T>::value, bool> = true,
std::enable_if_t<std::is_base_of<ndk::SharedRefBase, T>::value, bool> = true>
shared_ptr<T> make_shared(Args...) { // SEE COMMENT ABOVE.
static_assert(!std::is_base_of<ndk::SharedRefBase, T>::value);
}
template <typename T, typename... Args,
std::enable_if_t<ndk::internal::is_complete_type<T>::value, bool> = true,
std::enable_if_t<std::is_base_of<ndk::SharedRefBase, T>::value, bool> = true>
unique_ptr<T> make_unique(Args...) { // SEE COMMENT ABOVE.
static_assert(!std::is_base_of<ndk::SharedRefBase, T>::value);
}
} // namespace std
#endif
/** @} */

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_internal_logging.h
* @brief This provides the ability to use syslog from binder headers, since
* other logging functionality might be inaccessable.
*/
#pragma once
// defined differently by liblog
#pragma push_macro("LOG_PRI")
#ifdef LOG_PRI
#undef LOG_PRI
#endif
#include <syslog.h>
#pragma pop_macro("LOG_PRI")
/** @} */

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_parcelable_utils.h
* @brief Helper for parcelable.
*/
#pragma once
#include <android/binder_parcel_utils.h>
#include <optional>
namespace ndk {
// Also see Parcelable.h in libbinder.
typedef int32_t parcelable_stability_t;
enum {
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
#define RETURN_ON_FAILURE(expr) \
do { \
binder_status_t _status = (expr); \
if (_status != STATUS_OK) return _status; \
} while (false)
// AParcelableHolder has been introduced in 31.
#if __ANDROID_API__ >= 31
class AParcelableHolder {
public:
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
AParcelableHolder(const AParcelableHolder& other)
: mParcel(AParcel_create()), mStability(other.mStability) {
AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
AParcel_getDataSize(other.mParcel.get()));
}
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
int32_t size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
return STATUS_OK;
}
binder_status_t readFromParcel(const AParcel* parcel) {
AParcel_reset(mParcel.get());
parcelable_stability_t wireStability;
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability));
if (this->mStability != wireStability) {
return STATUS_BAD_VALUE;
}
int32_t dataSize;
binder_status_t status = AParcel_readInt32(parcel, &dataSize);
if (status != STATUS_OK || dataSize < 0) {
return status != STATUS_OK ? status : STATUS_BAD_VALUE;
}
int32_t dataStartPos = AParcel_getDataPosition(parcel);
if (dataStartPos > INT32_MAX - dataSize) {
return STATUS_BAD_VALUE;
}
status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
if (status != STATUS_OK) {
return status;
}
return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
}
template <typename T>
binder_status_t setParcelable(const T& p) {
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
AParcel_reset(mParcel.get());
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
p.writeToParcel(mParcel.get());
return STATUS_OK;
}
template <typename T>
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
if (AParcel_getDataSize(mParcel.get()) == 0) {
*ret = std::nullopt;
return STATUS_OK;
}
std::string parcelableDescInParcel;
binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
*ret = std::nullopt;
return status;
}
*ret = std::make_optional<T>();
status = (*ret)->readFromParcel(this->mParcel.get());
if (status != STATUS_OK) {
*ret = std::nullopt;
return status;
}
return STATUS_OK;
}
void reset() { AParcel_reset(mParcel.get()); }
inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
inline bool operator<=(const AParcelableHolder& rhs) const { return this <= &rhs; }
inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
inline AParcelableHolder& operator=(const AParcelableHolder& rhs) {
this->reset();
if (this->mStability != rhs.mStability) {
syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
this->mStability, rhs.mStability);
abort();
}
AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
AParcel_getDataSize(rhs.mParcel.get()));
return *this;
}
private:
mutable ndk::ScopedAParcel mParcel;
parcelable_stability_t mStability;
};
#endif // __ANDROID_API__ >= 31
#undef RETURN_ON_FAILURE
} // namespace ndk
/** @} */

View File

@@ -0,0 +1,244 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_to_string.h
* @brief Helper for parcelable.
*/
#pragma once
#include <codecvt>
#include <locale>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#if __has_include(<utils/StrongPointer.h>)
#include <utils/StrongPointer.h>
#define HAS_STRONG_POINTER
#endif
#if __has_include(<utils/String16.h>)
#include <utils/String16.h>
#define HAS_STRING16
#endif
#if __has_include(<android/binder_ibinder.h>)
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <android/binder_parcelable_utils.h>
#define HAS_NDK_INTERFACE
#endif
// TODO: some things include libbinder without having access to libbase. This is
// due to frameworks/native/include, which symlinks to libbinder headers, so even
// though we don't use it here, we detect a different header, so that we are more
// confident libbase will be included
#if __has_include(<binder/RpcSession.h>)
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#define HAS_CPP_INTERFACE
#endif
namespace android {
namespace internal {
// ToString is a utility to generate string representation for various AIDL-supported types.
template <typename _T>
std::string ToString(const _T& t);
namespace details {
// Truthy if _T has toString() method.
template <typename _T>
class HasToStringMethod {
template <typename _U>
static auto _test(int) -> decltype(std::declval<_U>().toString(), std::true_type());
template <typename _U>
static std::false_type _test(...);
public:
enum { value = decltype(_test<_T>(0))::value };
};
// Truthy if _T has a overloaded toString(T)
template <typename _T>
class HasToStringFunction {
template <typename _U>
static auto _test(int) -> decltype(toString(std::declval<_U>()), std::true_type());
template <typename _U>
static std::false_type _test(...);
public:
enum { value = decltype(_test<_T>(0))::value };
};
template <typename T, template <typename...> typename U>
struct IsInstantiationOf : std::false_type {};
template <template <typename...> typename U, typename... Args>
struct IsInstantiationOf<U<Args...>, U> : std::true_type {};
// Truthy if _T is like a pointer: one of sp/optional/shared_ptr
template <typename _T>
class IsPointerLike {
template <typename _U>
static std::enable_if_t<
#ifdef HAS_STRONG_POINTER
IsInstantiationOf<_U, sp>::value || // for IBinder and interface types in the C++
// backend
#endif
IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the
// C++/NDK backends
IsInstantiationOf<_U, std::unique_ptr>::value || // for @nullable(heap=true)
// in C++/NDK backends
IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the
// NDK backends
std::true_type>
_test(int);
template <typename _U>
static std::false_type _test(...);
public:
enum { value = decltype(_test<_T>(0))::value };
};
// Truthy if _T is like a container
template <typename _T>
class IsIterable {
template <typename _U>
static auto _test(int)
-> decltype(begin(std::declval<_U>()), end(std::declval<_U>()), std::true_type());
template <typename _U>
static std::false_type _test(...);
public:
enum { value = decltype(_test<_T>(0))::value };
};
template <typename _T>
struct TypeDependentFalse {
enum { value = false };
};
} // namespace details
template <typename _T>
std::string ToString(const _T& t) {
if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
// TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the
// warnings. There's no replacement in the standard library yet.
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"");
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t);
_Pragma("clang diagnostic pop");
} else if constexpr (std::is_arithmetic_v<_T>) {
return std::to_string(t);
} else if constexpr (std::is_same_v<std::string, _T>) {
return t;
#ifdef HAS_NDK_INTERFACE
} else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
std::stringstream ss;
ss << "binder:" << std::hex << t.get();
return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
return "fd:" + std::to_string(t.get());
} else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) {
// TODO(b/266248339): this format is to make it easy to handle resolv_integration_test
// freezing the output format. We would like to print more info.
return "<interface>";
#if __ANDROID_API__ >= 31
} else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) {
return "AParcelableHolder";
#endif
#endif // HAS_NDK_INTERFACE
#ifdef HAS_CPP_INTERFACE
} else if constexpr (std::is_base_of_v<IInterface, _T>) {
std::stringstream ss;
ss << "interface:" << std::hex << &t;
return ss.str();
} else if constexpr (std::is_same_v<IBinder, _T>) {
std::stringstream ss;
ss << "binder:" << std::hex << &t;
return ss.str();
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
std::stringstream out;
out << t;
return out.str();
#endif
} else if constexpr (details::IsPointerLike<_T>::value || std::is_pointer_v<_T>) {
if (!t) return "(null)";
std::stringstream out;
out << ToString(*t);
return out.str();
} else if constexpr (details::HasToStringMethod<_T>::value) {
return t.toString();
} else if constexpr (details::HasToStringFunction<_T>::value) {
return toString(t);
} else if constexpr (details::IsIterable<_T>::value) {
std::stringstream out;
bool first = true;
out << "[";
for (const auto& e : t) {
if (first) {
first = false;
} else {
out << ", ";
}
// Use explicit type parameter in case deref of iterator has different type
// e.g. vector<bool>
out << ToString<typename _T::value_type>(e);
}
out << "]";
return out.str();
} else {
static_assert(details::TypeDependentFalse<_T>::value, "no toString implemented, huh?");
}
}
} // namespace internal
} // namespace android
#ifdef HAS_STRONG_POINTER
#undef HAS_STRONG_POINTER
#endif
#ifdef HAS_STRING16
#undef HAS_STRING16
#endif
#ifdef HAS_NDK_INTERFACE
#undef HAS_NDK_INTERFACE
#endif
#ifdef HAS_CPP_INTERFACE
#undef HAS_CPP_INTERFACE
#endif
/** @} */