blob: d2a0a8adb5a046494c13444c27314d2900c7fb8a [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
#ifndef ANDROID_HIDL_SUPPORT_H
#define ANDROID_HIDL_SUPPORT_H
#include <algorithm>
#include <dirent.h>
#include <dlfcn.h>
#include <cutils/properties.h>
#include <functional>
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
#include <hwbinder/Parcel.h>
#include <map>
#include <tuple>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
namespace android {
namespace hardware {
struct hidl_string {
hidl_string();
~hidl_string();
// copy constructor.
hidl_string(const hidl_string &);
// copy from a C-style string.
hidl_string(const char *);
// copy from an std::string.
hidl_string(const std::string &);
// move constructor.
hidl_string(hidl_string &&);
const char *c_str() const;
size_t size() const;
bool empty() const;
// copy assignment operator.
hidl_string &operator=(const hidl_string &);
// copy from a C-style string.
hidl_string &operator=(const char *s);
// copy from an std::string.
hidl_string &operator=(const std::string &);
// move assignment operator.
hidl_string &operator=(hidl_string &&other);
// cast to std::string.
operator std::string() const;
// cast to C-style string. Caller is responsible
// to maintain this hidl_string alive.
operator const char *() const;
void clear();
// Reference an external char array. Ownership is _not_ transferred.
// Caller is responsible for ensuring that underlying memory is valid
// for the lifetime of this hidl_string.
void setToExternal(const char *data, size_t size);
// offsetof(hidl_string, mBuffer) exposed since mBuffer is private.
static const size_t kOffsetOfBuffer;
private:
const char *mBuffer;
size_t mSize; // NOT including the terminating '\0'.
bool mOwnsBuffer; // if true then mBuffer is a mutable char *
// copy from data with size. Assume that my memory is freed
// (through clear(), for example)
void copyFrom(const char *data, size_t size);
// move from another hidl_string
void moveFrom(hidl_string &&);
};
status_t readEmbeddedFromParcel(hidl_string *string,
const Parcel &parcel, size_t parentHandle, size_t parentOffset);
status_t writeEmbeddedToParcel(const hidl_string &string,
Parcel *parcel, size_t parentHandle, size_t parentOffset);
inline bool operator==(const hidl_string &hs, const char *s) {
return strcmp(hs.c_str(), s) == 0;
}
inline bool operator!=(const hidl_string &hs, const char *s) {
return !(hs == s);
}
inline bool operator==(const char *s, const hidl_string &hs) {
return strcmp(hs.c_str(), s) == 0;
}
inline bool operator!=(const char *s, const hidl_string &hs) {
return !(s == hs);
}
////////////////////////////////////////////////////////////////////////////////
template<typename T>
struct hidl_vec {
hidl_vec()
: mBuffer(NULL),
mSize(0),
mOwnsBuffer(true) {
}
hidl_vec(const hidl_vec<T> &other) : hidl_vec() {
*this = other;
}
hidl_vec(hidl_vec<T> &&other)
: mOwnsBuffer(false) {
*this = std::move(other);
}
hidl_vec(const std::initializer_list<int> list)
: mSize(list.size()),
mOwnsBuffer(true) {
mBuffer = new T[mSize];
int idx = 0;
for (auto it = list.begin(); it != list.end(); ++it) {
mBuffer[idx++] = *it;
}
}
hidl_vec(const std::vector<T> &other) : hidl_vec() {
*this = other;
}
~hidl_vec() {
if (mOwnsBuffer) {
delete[] mBuffer;
}
mBuffer = NULL;
}
// Reference an existing array, optionally taking ownership. It is the
// caller's responsibility to ensure that the underlying memory stays
// valid for the lifetime of this hidl_vec.
void setToExternal(T *data, size_t size, bool shouldOwn = false) {
if (mOwnsBuffer) {
delete [] mBuffer;
}
mBuffer = data;
mSize = size;
mOwnsBuffer = shouldOwn;
}
T *data() {
return mBuffer;
}
const T *data() const {
return mBuffer;
}
T *releaseData() {
if (!mOwnsBuffer && mSize > 0) {
resize(mSize);
}
mOwnsBuffer = false;
return mBuffer;
}
hidl_vec &operator=(hidl_vec &&other) {
if (mOwnsBuffer) {
delete[] mBuffer;
}
mBuffer = other.mBuffer;
mSize = other.mSize;
mOwnsBuffer = other.mOwnsBuffer;
other.mOwnsBuffer = false;
return *this;
}
hidl_vec &operator=(const hidl_vec &other) {
if (this != &other) {
if (mOwnsBuffer) {
delete[] mBuffer;
}
copyFrom(other, other.mSize);
}
return *this;
}
// copy from an std::vector.
hidl_vec &operator=(const std::vector<T> &other) {
if (mOwnsBuffer) {
delete[] mBuffer;
}
copyFrom(other, other.size());
return *this;
}
// cast to an std::vector.
operator std::vector<T>() const {
std::vector<T> v(mSize);
for (size_t i = 0; i < mSize; ++i) {
v[i] = mBuffer[i];
}
return v;
}
size_t size() const {
return mSize;
}
T &operator[](size_t index) {
return mBuffer[index];
}
const T &operator[](size_t index) const {
return mBuffer[index];
}
void resize(size_t size) {
T *newBuffer = new T[size];
for (size_t i = 0; i < std::min(size, mSize); ++i) {
newBuffer[i] = mBuffer[i];
}
if (mOwnsBuffer) {
delete[] mBuffer;
}
mBuffer = newBuffer;
mSize = size;
mOwnsBuffer = true;
}
status_t findInParcel(const Parcel &parcel, size_t *handle) const {
return parcel.quickFindBuffer(mBuffer, handle);
}
// offsetof(hidl_string, mBuffer) exposed since mBuffer is private.
static const size_t kOffsetOfBuffer;
private:
T *mBuffer;
size_t mSize;
bool mOwnsBuffer;
// copy from an array-like object, assuming my resources are freed.
template <typename Array>
void copyFrom(const Array &data, size_t size) {
mSize = size;
mOwnsBuffer = true;
if (mSize > 0) {
mBuffer = new T[size];
for (size_t i = 0; i < size; ++i) {
mBuffer[i] = data[i];
}
} else {
mBuffer = NULL;
}
}
};
template <typename T>
const size_t hidl_vec<T>::kOffsetOfBuffer = offsetof(hidl_vec<T>, mBuffer);
template<typename T>
status_t readEmbeddedFromParcel(
hidl_vec<T> * /*vec*/,
const Parcel &parcel,
size_t parentHandle,
size_t parentOffset,
size_t *handle) {
const void *ptr = parcel.readEmbeddedBuffer(
handle,
parentHandle,
parentOffset + hidl_vec<T>::kOffsetOfBuffer);
return ptr != NULL ? OK : UNKNOWN_ERROR;
}
template<typename T>
status_t writeEmbeddedToParcel(
const hidl_vec<T> &vec,
Parcel *parcel,
size_t parentHandle,
size_t parentOffset,
size_t *handle) {
return parcel->writeEmbeddedBuffer(
vec.data(),
sizeof(T) * vec.size(),
handle,
parentHandle,
parentOffset + hidl_vec<T>::kOffsetOfBuffer);
}
////////////////////////////////////////////////////////////////////////////////
namespace details {
template<size_t SIZE1, size_t... SIZES>
struct product {
static constexpr size_t value = SIZE1 * product<SIZES...>::value;
};
template<size_t SIZE1>
struct product<SIZE1> {
static constexpr size_t value = SIZE1;
};
template<typename T, size_t SIZE1, size_t... SIZES>
struct accessor {
explicit accessor(T *base)
: mBase(base) {
}
accessor<T, SIZES...> operator[](size_t index) {
return accessor<T, SIZES...>(
&mBase[index * product<SIZES...>::value]);
}
private:
T *mBase;
};
template<typename T, size_t SIZE1>
struct accessor<T, SIZE1> {
explicit accessor(T *base)
: mBase(base) {
}
T &operator[](size_t index) {
return mBase[index];
}
private:
T *mBase;
};
template<typename T, size_t SIZE1, size_t... SIZES>
struct const_accessor {
explicit const_accessor(const T *base)
: mBase(base) {
}
const_accessor<T, SIZES...> operator[](size_t index) {
return const_accessor<T, SIZES...>(
&mBase[index * product<SIZES...>::value]);
}
private:
const T *mBase;
};
template<typename T, size_t SIZE1>
struct const_accessor<T, SIZE1> {
explicit const_accessor(const T *base)
: mBase(base) {
}
const T &operator[](size_t index) const {
return mBase[index];
}
private:
const T *mBase;
};
} // namespace details
////////////////////////////////////////////////////////////////////////////////
template<typename T, size_t SIZE1, size_t... SIZES>
struct hidl_array {
hidl_array() = default;
T *data() { return mBuffer; }
const T *data() const { return mBuffer; }
details::accessor<T, SIZES...> operator[](size_t index) {
return details::accessor<T, SIZES...>(
&mBuffer[index * details::product<SIZES...>::value]);
}
details::const_accessor<T, SIZES...> operator[](size_t index) const {
return details::const_accessor<T, SIZES...>(
&mBuffer[index * details::product<SIZES...>::value]);
}
using size_tuple_type = std::tuple<decltype(SIZE1), decltype(SIZES)...>;
static constexpr size_tuple_type size() {
return std::make_tuple(SIZE1, SIZES...);
}
private:
T mBuffer[details::product<SIZE1, SIZES...>::value];
};
template<typename T, size_t SIZE1>
struct hidl_array<T, SIZE1> {
hidl_array() = default;
T *data() { return mBuffer; }
const T *data() const { return mBuffer; }
T &operator[](size_t index) {
return mBuffer[index];
}
const T &operator[](size_t index) const {
return mBuffer[index];
}
static constexpr size_t size() { return SIZE1; }
private:
T mBuffer[SIZE1];
};
///////////////////////////// pointers for HIDL
template <typename T>
static status_t readEmbeddedReferenceFromParcel(
T const* * /* bufptr */,
const Parcel & parcel,
size_t parentHandle,
size_t parentOffset,
size_t *handle,
bool *shouldResolveRefInBuffer
) {
// *bufptr is ignored because, if I am embedded in some
// other buffer, the kernel should have fixed me up already.
bool isPreviouslyWritten;
status_t result = parcel.readEmbeddedReference(
nullptr, // ignored, not written to bufptr.
handle,
parentHandle,
parentOffset,
&isPreviouslyWritten);
// tell caller to run T::readEmbeddedToParcel and
// T::readEmbeddedReferenceToParcel if necessary.
// It is not called here because we don't know if these two are valid methods.
*shouldResolveRefInBuffer = !isPreviouslyWritten;
return result;
}
template <typename T>
static status_t writeEmbeddedReferenceToParcel(
T const* buf,
Parcel *parcel, size_t parentHandle, size_t parentOffset,
size_t *handle,
bool *shouldResolveRefInBuffer
) {
if(buf == nullptr) {
*shouldResolveRefInBuffer = false;
return parcel->writeEmbeddedNullReference(handle, parentHandle, parentOffset);
}
// find whether the buffer exists
size_t childHandle, childOffset;
status_t result;
bool found;
result = parcel->findBuffer(buf, sizeof(T), &found, &childHandle, &childOffset);
// tell caller to run T::writeEmbeddedToParcel and
// T::writeEmbeddedReferenceToParcel if necessary.
// It is not called here because we don't know if these two are valid methods.
*shouldResolveRefInBuffer = !found;
if(result != OK) {
return result; // bad pointers and length given
}
if(!found) { // did not find it.
return parcel->writeEmbeddedBuffer(buf, sizeof(T), handle,
parentHandle, parentOffset);
}
// found the buffer. easy case.
return parcel->writeEmbeddedReference(
handle,
childHandle,
childOffset,
parentHandle,
parentOffset);
}
template <typename T>
static status_t readReferenceFromParcel(
T const* *bufptr,
const Parcel & parcel,
size_t *handle,
bool *shouldResolveRefInBuffer
) {
bool isPreviouslyWritten;
status_t result = parcel.readReference(reinterpret_cast<void const* *>(bufptr),
handle, &isPreviouslyWritten);
// tell caller to run T::readEmbeddedToParcel and
// T::readEmbeddedReferenceToParcel if necessary.
// It is not called here because we don't know if these two are valid methods.
*shouldResolveRefInBuffer = !isPreviouslyWritten;
return result;
}
template <typename T>
static status_t writeReferenceToParcel(
T const *buf,
Parcel * parcel,
size_t *handle,
bool *shouldResolveRefInBuffer
) {
if(buf == nullptr) {
*shouldResolveRefInBuffer = false;
return parcel->writeNullReference(handle);
}
// find whether the buffer exists
size_t childHandle, childOffset;
status_t result;
bool found;
result = parcel->findBuffer(buf, sizeof(T), &found, &childHandle, &childOffset);
// tell caller to run T::writeEmbeddedToParcel and
// T::writeEmbeddedReferenceToParcel if necessary.
// It is not called here because we don't know if these two are valid methods.
*shouldResolveRefInBuffer = !found;
if(result != OK) {
return result; // bad pointers and length given
}
if(!found) { // did not find it.
return parcel->writeBuffer(buf, sizeof(T), handle);
}
// found the buffer. easy case.
return parcel->writeReference(handle,
childHandle, childOffset);
}
// ----------------------------------------------------------------------
// Version functions
struct hidl_version {
public:
constexpr hidl_version(uint16_t major, uint16_t minor) : mMajor(major), mMinor(minor) {}
bool operator==(const hidl_version& other) const {
return (mMajor == other.get_major() && mMinor == other.get_minor());
}
constexpr uint16_t get_major() const { return mMajor; }
constexpr uint16_t get_minor() const { return mMinor; }
android::status_t writeToParcel(android::hardware::Parcel& parcel) const {
return parcel.writeUint32(static_cast<uint32_t>(mMajor) << 16 | mMinor);
}
static hidl_version* readFromParcel(const android::hardware::Parcel& parcel) {
uint32_t version;
android::status_t status = parcel.readUint32(&version);
if (status != OK) {
return nullptr;
} else {
return new hidl_version(version >> 16, version & 0xFFFF);
}
}
private:
uint16_t mMajor;
uint16_t mMinor;
};
inline android::hardware::hidl_version make_hidl_version(uint16_t major, uint16_t minor) {
return hidl_version(major,minor);
}
struct IBase : virtual public RefBase {
virtual bool isRemote() const = 0;
// HIDL reserved methods follow.
virtual ::android::hardware::Return<void> interfaceChain(
std::function<void(const hidl_vec<hidl_string>&)> _hidl_cb) = 0;
// descriptor for HIDL reserved methods.
static const char* descriptor;
};
extern std::map<std::string, std::function<sp<IBinder>(void*)>> gBnConstructorMap;
// Construct a smallest possible binder from the given interface.
// If it is remote, then its remote() will be retrieved.
// Otherwise, the smallest possible BnChild is found where IChild is a subclass of IType
// and iface is of class IChild. BnChild will be used to wrapped the given iface.
// Return nullptr if iface is null or any failure.
template <typename IType, typename IHwType>
sp<IBinder> toBinder(sp<IType> iface) {
IType *ifacePtr = iface.get();
if (ifacePtr == nullptr) {
return nullptr;
}
if (ifacePtr->isRemote()) {
return ::android::hardware::IInterface::asBinder(static_cast<IHwType *>(ifacePtr));
} else {
std::string myDescriptor{};
ifacePtr->interfaceChain([&](const hidl_vec<hidl_string> &types) {
if (types.size() > 0) {
myDescriptor = types[0].c_str();
}
});
if (myDescriptor.empty()) {
// interfaceChain fails || types.size() == 0
return nullptr;
}
auto iter = gBnConstructorMap.find(myDescriptor);
if (iter == gBnConstructorMap.end()) {
return nullptr;
}
return sp<IBinder>((iter->second)(reinterpret_cast<void *>(ifacePtr)));
}
}
// cast the interface IParent to IChild.
// Return nullptr if parent is null or any failure.
template<typename IChild, typename IParent, typename BpChild, typename IHwParent>
sp<IChild> castInterface(sp<IParent> parent, const char *childIndicator) {
if (parent.get() == nullptr) {
// casts always succeed with nullptrs.
return nullptr;
}
bool canCast = false;
parent->interfaceChain([&](const hidl_vec<hidl_string> &allowedCastTypes) {
for (size_t i = 0; i < allowedCastTypes.size(); i++) {
if (allowedCastTypes[i] == childIndicator) {
canCast = true;
break;
}
}
});
if (!canCast) {
return sp<IChild>(nullptr); // cast failed.
}
if (parent->isRemote()) {
// binderized mode. Got BpChild. grab the remote and wrap it.
return sp<IChild>(new BpChild(toBinder<IParent, IHwParent>(parent)));
}
// Passthrough mode. Got BnChild and BsChild.
return sp<IChild>(static_cast<IChild *>(parent.get()));
}
#if defined(__LP64__)
#define HAL_LIBRARY_PATH_SYSTEM "/system/lib64/hw/"
#define HAL_LIBRARY_PATH_VENDOR "/vendor/lib64/hw/"
#define HAL_LIBRARY_PATH_ODM "/odm/lib64/hw/"
#else
#define HAL_LIBRARY_PATH_SYSTEM "/system/lib/hw/"
#define HAL_LIBRARY_PATH_VENDOR "/vendor/lib/hw/"
#define HAL_LIBRARY_PATH_ODM "/odm/lib/hw/"
#endif
#define DECLARE_SERVICE_MANAGER_INTERACTIONS(INTERFACE) \
static ::android::sp<I##INTERFACE> getService( \
const std::string &serviceName, bool getStub=false); \
::android::status_t registerAsService(const std::string &serviceName); \
static bool registerForNotifications( \
const std::string &serviceName, \
const ::android::sp<::android::hidl::manager::V1_0::IServiceNotification> \
&notification); \
#define IMPLEMENT_SERVICE_MANAGER_INTERACTIONS(INTERFACE, PACKAGE) \
::android::sp<I##INTERFACE> I##INTERFACE::getService( \
const std::string &serviceName, bool getStub) \
{ \
using ::android::sp; \
using ::android::hardware::defaultServiceManager; \
using ::android::hardware::IBinder; \
using ::android::hidl::manager::V1_0::IServiceManager; \
sp<I##INTERFACE> iface; \
const sp<IServiceManager> sm = defaultServiceManager(); \
if (sm != nullptr && !getStub) { \
sp<IBinder> binderIface; \
::android::hardware::Return<void> ret = \
sm->get(PACKAGE "::I" #INTERFACE, serviceName.c_str(), \
[&binderIface](sp<IBinder> iface) { \
binderIface = iface; \
}); \
if (ret.getStatus().isOk()) { \
iface = IHw##INTERFACE::asInterface(binderIface); \
if (iface != nullptr) { \
return iface; \
} \
} \
} \
int dlMode = RTLD_LAZY; \
void *handle = dlopen(HAL_LIBRARY_PATH_ODM PACKAGE "-impl.so", dlMode); \
if (handle == nullptr) { \
handle = dlopen(HAL_LIBRARY_PATH_VENDOR PACKAGE "-impl.so", dlMode); \
} \
if (handle == nullptr) { \
handle = dlopen(HAL_LIBRARY_PATH_SYSTEM PACKAGE "-impl.so", dlMode); \
} \
if (handle == nullptr) { \
return iface; \
} \
I##INTERFACE* (*generator)(const char* name); \
*(void **)(&generator) = dlsym(handle, "HIDL_FETCH_I"#INTERFACE); \
if (generator) { \
iface = (*generator)(serviceName.c_str()); \
if (iface != nullptr) { \
iface = new Bs##INTERFACE(iface); \
} \
} \
return iface; \
} \
::android::status_t I##INTERFACE::registerAsService( \
const std::string &serviceName) \
{ \
using ::android::sp; \
using ::android::hardware::defaultServiceManager; \
using ::android::hidl::manager::V1_0::IServiceManager; \
sp<Bn##INTERFACE> binderIface = new Bn##INTERFACE(this); \
const sp<IServiceManager> sm = defaultServiceManager(); \
bool success = false; \
::android::hardware::Return<void> ret = \
this->interfaceChain( \
[&success, &sm, &serviceName, &binderIface](const auto &chain) { \
success = sm->add(chain, serviceName.c_str(), binderIface); \
}); \
success = success && ret.getStatus().isOk(); \
return success ? ::android::OK : ::android::UNKNOWN_ERROR; \
} \
bool I##INTERFACE::registerForNotifications( \
const std::string &serviceName, \
const ::android::sp<::android::hidl::manager::V1_0::IServiceNotification> \
&notification) \
{ \
using ::android::sp; \
using ::android::hardware::defaultServiceManager; \
using ::android::hidl::manager::V1_0::IServiceManager; \
const sp<IServiceManager> sm = defaultServiceManager(); \
if (sm == nullptr) { \
return false; \
} \
return sm->registerForNotifications(PACKAGE "::I" #INTERFACE, \
serviceName, \
notification); \
}
// ----------------------------------------------------------------------
// Class that provides Hidl instrumentation utilities.
struct HidlInstrumentor {
// Event that triggers the instrumentation. e.g. enter of an API call on
// the server/client side, exit of an API call on the server/client side
// etc.
enum InstrumentationEvent {
SERVER_API_ENTRY = 0,
SERVER_API_EXIT,
CLIENT_API_ENTRY,
CLIENT_API_EXIT,
SYNC_CALLBACK_ENTRY,
SYNC_CALLBACK_EXIT,
ASYNC_CALLBACK_ENTRY,
ASYNC_CALLBACK_EXIT,
PASSTHROUGH_ENTRY,
PASSTHROUGH_EXIT,
};
// Signature of the instrumentation callback function.
using InstrumentationCallback = std::function<void(
const InstrumentationEvent event,
const char *package,
const char *version,
const char *interface,
const char *method,
std::vector<void *> *args)>;
explicit HidlInstrumentor(const std::string &prefix);
virtual ~HidlInstrumentor();
protected:
// Function that lookup and dynamically loads the hidl instrumentation
// libraries and registers the instrumentation callback functions.
//
// The instrumentation libraries should be stored under any of the following
// directories: HAL_LIBRARY_PATH_SYSTEM, HAL_LIBRARY_PATH_VENDOR and
// HAL_LIBRARY_PATH_ODM. The name of instrumentation libraries should
// follow pattern: ^profilerPrefix(.*).profiler.so$
//
// Each instrumentation library is expected to implement the instrumentation
// function called HIDL_INSTRUMENTATION_FUNCTION.
//
// A no-op for user build.
void registerInstrumentationCallbacks(
const std::string &profilerPrefix,
std::vector<InstrumentationCallback> *instrumentationCallbacks);
// Utility function to determine whether a give file is a instrumentation
// library (i.e. the file name follow the expected pattern).
bool isInstrumentationLib(
const std::string &profilerPrefix,
const dirent *file);
// A list of registered instrumentation callbacks.
std::vector<InstrumentationCallback> mInstrumentationCallbacks;
// Flag whether to enable instrumentation.
bool mEnableInstrumentation;
};
} // namespace hardware
} // namespace android
#endif // ANDROID_HIDL_SUPPORT_H