/* * 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