/*
* Copyright (C) 2017 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_HYBRIDINTERFACE_H
#define ANDROID_HYBRIDINTERFACE_H
#include <vector>
#include <mutex>
#include <binder/Parcel.h>
#include <hidl/HidlSupport.h>
/**
* Hybrid Interfaces
* =================
*
* A hybrid interface is a binder interface that
* 1. is implemented both traditionally and as a wrapper around a hidl
* interface, and allows querying whether the underlying instance comes from
* a hidl interface or not; and
* 2. allows efficient calls to a hidl interface (if the underlying instance
* comes from a hidl interface) by automatically creating the wrapper in the
* process that calls it.
*
* Terminology:
* - `HalToken`: The type for a "token" of a hidl interface. This is defined to
* be compatible with `ITokenManager.hal`.
* - `HInterface`: The base type for a hidl interface. Currently, it is defined
* as `::android::hidl::base::V1_0::IBase`.
* - `HALINTERFACE`: The hidl interface that will be sent through binders.
* - `INTERFACE`: The binder interface that will be the wrapper of
* `HALINTERFACE`. `INTERFACE` is supposed to be somewhat similar to
* `HALINTERFACE`.
*
* To demonstrate how this is done, here is an example. Suppose `INTERFACE` is
* `IFoo` and `HALINTERFACE` is `HFoo`. The required steps are:
* 1. Use DECLARE_HYBRID_META_INTERFACE instead of DECLARE_META_INTERFACE in the
* definition of `IFoo`. The usage is
* DECLARE_HYBRID_META_INTERFACE(IFoo, HFoo)
* inside the body of `IFoo`.
* 2. Create a converter class that derives from
* `H2BConverter<HFoo, IFoo, BnFoo>`. Let us call this `H2BFoo`.
* 3. Add the following constructor in `H2BFoo` that call the corresponding
* constructors in `H2BConverter`:
* H2BFoo(const sp<HalInterface>& base) : CBase(base) {}
* Note: `CBase = H2BConverter<HFoo, IFoo, BnFoo>` and `HalInterface = HFoo`
* are member typedefs of `H2BConverter<HFoo, IFoo, BnFoo>`, so the above
* line can be copied into `H2BFoo`.
* 4. Implement `IFoo` in `H2BFoo` on top of `HFoo`. `H2BConverter` provides a
* protected `mBase` of type `sp<HFoo>` that can be used to access the `HFoo`
* instance. (There is also a public function named `getHalInterface()` that
* returns `mBase`.)
* 5. Create a hardware proxy class that derives from
* `HpInterface<BpFoo, H2BFoo>`. Name this class `HpFoo`. (This name cannot
* deviate. See step 8 below.)
* 6. Add the following constructor to `HpFoo`:
* HpFoo(const sp<IBinder>& base): PBase(base) {}
* Note: `PBase` a member typedef of `HpInterface<BpFoo, H2BFoo>` that is
* equal to `HpInterface<BpFoo, H2BFoo>` itself, so the above line can be
* copied verbatim into `HpFoo`.
* 7. Delegate all functions in `HpFoo` that come from `IFoo` except
* `getHalInterface` to the protected member `mBase`,
* which is defined in `HpInterface<BpFoo, H2BFoo>` (hence in `HpFoo`) with
* type `IFoo`. (There is also a public function named `getBaseInterface()`
* that returns `mBase`.)
* 8. Replace the existing `IMPLEMENT_META_INTERFACE` for INTERFACE by
* `IMPLEMENT_HYBRID_META_INTERFACE`. Note that this macro relies on the
* exact naming of `HpFoo`, where `Foo` comes from the interface name `IFoo`.
* An example usage is
* IMPLEMENT_HYBRID_META_INTERFACE(IFoo, HFoo, "example.interface.foo");
*
* `GETTOKEN` Template Argument
* ============================
*
* Following the instructions above, `H2BConverter` and `HpInterface` would use
* `transact()` to send over tokens, with `code` (the first argument of
* `transact()`) equal to `DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE`. If this
* value clashes with other values already in use in the `Bp` class, it can be
* changed by supplying the last optional template argument to `H2BConverter`
* and `HpInterface`.
*
*/
namespace android {
typedef ::android::hardware::hidl_vec<uint8_t> HalToken;
typedef ::android::hidl::base::V1_0::IBase HInterface;
constexpr uint32_t DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE =
B_PACK_CHARS('_', 'G', 'H', 'T');
sp<HInterface> retrieveHalInterface(const HalToken& token);
bool createHalToken(const sp<HInterface>& interface, HalToken* token);
bool deleteHalToken(const HalToken& token);
template <
typename HINTERFACE,
typename INTERFACE,
typename BNINTERFACE,
uint32_t GETTOKEN = DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE>
class H2BConverter : public BNINTERFACE {
public:
typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN> CBase; // Converter Base
typedef INTERFACE BaseInterface;
typedef HINTERFACE HalInterface;
static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
virtual status_t onTransact(uint32_t code,
const Parcel& data, Parcel* reply, uint32_t flags = 0);
virtual sp<HalInterface> getHalInterface() { return mBase; }
HalInterface* getBaseInterface() { return mBase.get(); }
virtual status_t linkToDeath(
const sp<IBinder::DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0);
virtual status_t unlinkToDeath(
const wp<IBinder::DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0,
wp<IBinder::DeathRecipient>* outRecipient = NULL);
protected:
sp<HalInterface> mBase;
struct Obituary : public hardware::hidl_death_recipient {
wp<IBinder::DeathRecipient> recipient;
void* cookie;
uint32_t flags;
wp<IBinder> who;
Obituary(
const wp<IBinder::DeathRecipient>& r,
void* c, uint32_t f,
const wp<IBinder>& w) :
recipient(r), cookie(c), flags(f), who(w) {
}
Obituary(const Obituary& o) :
recipient(o.recipient),
cookie(o.cookie),
flags(o.flags),
who(o.who) {
}
Obituary& operator=(const Obituary& o) {
recipient = o.recipient;
cookie = o.cookie;
flags = o.flags;
who = o.who;
return *this;
}
void serviceDied(uint64_t, const wp<HInterface>&) override {
sp<IBinder::DeathRecipient> dr = recipient.promote();
if (dr != nullptr) {
dr->binderDied(who);
}
}
};
std::mutex mObituariesLock;
std::vector<sp<Obituary> > mObituaries;
};
template <
typename BPINTERFACE,
typename CONVERTER,
uint32_t GETTOKEN = DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE>
class HpInterface : public CONVERTER::BaseInterface {
public:
typedef HpInterface<BPINTERFACE, CONVERTER, GETTOKEN> PBase; // Proxy Base
typedef typename CONVERTER::BaseInterface BaseInterface;
typedef typename CONVERTER::HalInterface HalInterface;
static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
explicit HpInterface(const sp<IBinder>& impl);
virtual sp<HalInterface> getHalInterface() { return mHal; }
BaseInterface* getBaseInterface() { return mBase.get(); }
protected:
IBinder* mImpl;
sp<BPINTERFACE> mBp;
sp<BaseInterface> mBase;
sp<HalInterface> mHal;
IBinder* onAsBinder() override { return mImpl; }
};
// ----------------------------------------------------------------------
#define DECLARE_HYBRID_META_INTERFACE(INTERFACE, HAL) \
static const ::android::String16 descriptor; \
static ::android::sp<I##INTERFACE> asInterface( \
const ::android::sp<::android::IBinder>& obj); \
virtual const ::android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
virtual sp<HAL> getHalInterface(); \
#define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, HAL, NAME) \
const ::android::String16 I##INTERFACE::descriptor(NAME); \
const ::android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const ::android::sp<::android::IBinder>& obj) \
{ \
::android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Hp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE() { } \
sp<HAL> I##INTERFACE::getHalInterface() { return nullptr; } \
// ----------------------------------------------------------------------
template <
typename HINTERFACE,
typename INTERFACE,
typename BNINTERFACE,
uint32_t GETTOKEN>
status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
if (code == GET_HAL_TOKEN) {
HalToken token;
bool result;
result = createHalToken(mBase, &token);
if (!result) {
ALOGE("H2BConverter: Failed to create HAL token.");
}
reply->writeBool(result);
reply->writeByteArray(token.size(), token.data());
return NO_ERROR;
}
return BNINTERFACE::onTransact(code, data, reply, flags);
}
template <
typename HINTERFACE,
typename INTERFACE,
typename BNINTERFACE,
uint32_t GETTOKEN>
status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
linkToDeath(
const sp<IBinder::DeathRecipient>& recipient,
void* cookie, uint32_t flags) {
LOG_ALWAYS_FATAL_IF(recipient == NULL,
"linkToDeath(): recipient must be non-NULL");
{
std::lock_guard<std::mutex> lock(mObituariesLock);
mObituaries.push_back(new Obituary(recipient, cookie, flags, this));
if (!mBase->linkToDeath(mObituaries.back(), 0)) {
return DEAD_OBJECT;
}
}
return NO_ERROR;
}
template <
typename HINTERFACE,
typename INTERFACE,
typename BNINTERFACE,
uint32_t GETTOKEN>
status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
unlinkToDeath(
const wp<IBinder::DeathRecipient>& recipient,
void* cookie, uint32_t flags,
wp<IBinder::DeathRecipient>* outRecipient) {
std::lock_guard<std::mutex> lock(mObituariesLock);
for (auto i = mObituaries.begin(); i != mObituaries.end(); ++i) {
if ((flags = (*i)->flags) && (
(recipient == (*i)->recipient) ||
((recipient == nullptr) && (cookie == (*i)->cookie)))) {
if (outRecipient != nullptr) {
*outRecipient = (*i)->recipient;
}
bool success = mBase->unlinkToDeath(*i);
mObituaries.erase(i);
return success ? NO_ERROR : DEAD_OBJECT;
}
}
return NAME_NOT_FOUND;
}
template <typename BPINTERFACE, typename CONVERTER, uint32_t GETTOKEN>
HpInterface<BPINTERFACE, CONVERTER, GETTOKEN>::HpInterface(
const sp<IBinder>& impl) :
mImpl(impl.get()),
mBp(new BPINTERFACE(impl)) {
mBase = mBp;
if (mImpl->remoteBinder() == nullptr) {
return;
}
Parcel data, reply;
data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor());
if (mImpl->transact(GET_HAL_TOKEN, data, &reply) == NO_ERROR) {
bool tokenCreated = reply.readBool();
std::vector<uint8_t> tokenVector;
reply.readByteVector(&tokenVector);
HalToken token = HalToken(tokenVector);
if (tokenCreated) {
sp<HInterface> hBase = retrieveHalInterface(token);
if (hBase != nullptr) {
mHal = HalInterface::castFrom(hBase);
if (mHal != nullptr) {
mBase = new CONVERTER(mHal);
} else {
ALOGE("HpInterface: Wrong interface type.");
}
} else {
ALOGE("HpInterface: Invalid HAL token.");
}
deleteHalToken(token);
}
}
}
// ----------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_HYBRIDINTERFACE_H