| /* |
| * 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> |
| |
| #include <cinttypes> |
| #include <variant> |
| |
| /** |
| * 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 declaration of `IFoo`. `DECLARE_HYBRID_META_INTERFACE` takes an |
| * additional argument that is the hidl interface to be converted into a |
| * binder interface. Example: |
| * Change from `DECLARE_META_INTERFACE(Foo)` |
| * to `DECLARE_HYBRID_META_INTERFACE(Foo, HFoo)` |
| * 2. Create a converter class that derives from |
| * `H2BConverter<HFoo, 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, BnFoo>` and `HalInterface = HFoo` are |
| * member typedefs of `H2BConverter<HFoo, BnFoo>`, so the above line can be |
| * copied verbatim 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 `getBase()` 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 those that |
| * are defined by the macro `DECLARE_HYBRID_META_INTERFACE`) to the protected |
| * member `mBase`. `mBase` is defined in `HpInterface<BpFoo, H2BFoo>` (hence |
| * in `HpFoo`) with type `IFoo`. There is also a public function named |
| * `getBase()` that returns `mBase`. |
| * 8. Replace the existing `IMPLEMENT_META_INTERFACE` for `IFoo` by |
| * `IMPLEMENT_HYBRID_META_INTERFACE`. This macro assumes that the subclass of |
| * `HpInterface` for `IFoo` is named `HpFoo`. |
| * |
| * After the hybrid interface has been put in place properly, it can be used to |
| * do the following tasks: |
| * 1. Create an `IFoo` instance from an `HFoo` by passing `sp<HFoo>` to |
| * the constructor of `H2BFoo`. |
| * 2. Retrieve an `HFoo` from an `HpFoo` instance by calling |
| * `HpFoo::getHalInterface<HFoo>()`. This function may return `nullptr` if |
| * the `HpFoo` object is not backed by `HFoo`. The template parameter is |
| * required because `HpFoo` in fact may be backed by multiple H2B converter |
| * classes. |
| * |
| * Multiple H2B Converters |
| * ======================= |
| * |
| * Because the system may support multiple versions of hidl interfaces for the |
| * same object, one binder interface may correspond to multiple H2B converters. |
| * The hybrid interface is designed to handle this as |
| * well---`DECLARE_HYBRID_META_INTERFACE` and `HpInterface` can take a variable |
| * number of arguments. |
| * |
| * As a concrete example, suppose `IFoo` is a binder interface that corresponds |
| * to two hidl interfaces `HFoo1` and `HFoo2`. That means `HpFoo`, the hybrid |
| * interface presenting `IFoo`, may be backed by `HFoo1` or `HFoo2`. This is |
| * achievable by |
| * |
| * - Replacing `DECLARE_META_INTERFACE(Foo)` by |
| * `DECLARE_HYBRID_META_INTERFACE(Foo, HFoo1, HFoo2)` in the declaration of |
| * `IFoo`. |
| * - Creating `H2BFoo1` as a subclass of `H2BConverter<HFoo1, BnFoo>`; |
| * - Creating `H2BFoo2` as a subclass of `H2BConverter<HFoo2, BnFoo>`; and |
| * - Creating `HpFoo` as a subclass of `HpInterface<BpFoo, H2BFoo1, H2BFoo2>`. |
| * |
| * It is important that `HFoo1` and `HFoo2` are different hidl interfaces. [The |
| * actual requirement is that for each pair `<HFoo, IFoo>`, there can be only |
| * one subclass of `H2BConverter<HFoo, BnFoo>`.] |
| * |
| * As mentioned in the previous section, `HpFoo::getHalInterface` requires a |
| * template argument because it must be able to return different hidl |
| * interface types based on which hidl interface is being used. The user of |
| * `HpFoo` can query the type of the underlying hidl interface by calling |
| * `HpFoo::getHalIndex()`. The return value is a 1-based index into the list of |
| * all the supported hidl interfaces. In the example with 2 hidl interfaces |
| * `HFoo1` and `HFoo2`, index 1 corresponds to `HFoo1` and index 2 corresponds |
| * to `HFoo2`. A typical code block that accesses the underlying hidl interface |
| * of would look like this: |
| * |
| * void someFunction(const sp<IFoo> &foo) { |
| * |
| * switch (foo->getHalIndex()) { |
| * case 1: { |
| * sp<HFoo1> hFoo1 = foo->getHalInterface<HFoo1>(); |
| * ... |
| * break; |
| * } |
| * case 2: { |
| * sp<HFoo2> hFoo2 = foo->getHalInterface<HFoo2>(); |
| * ... |
| * break; |
| * } |
| * default: // Not backed by a hidl interface. |
| * // Alternatively, "case 0:" can be used. |
| * } |
| * |
| * } |
| * |
| * Error State |
| * =========== |
| * |
| * A corrupted transaction may cause an `HpInterface` to be in an error state. |
| * This could cause `getHalInterface<ExpectedHalInterface>()` to return |
| * `nullptr` even though `getHalIndex()` returns a non-zero index and |
| * `ExpectedHalInterface` is the corresponding hidl interface. It is therefore |
| * recommended that a null check be performed on the return value of |
| * `getHalInterface` before using it. |
| * |
| * DECLARE_HYBRID_META_INTERFACE_WITH_CODE |
| * ======================================= |
| * |
| * `H2BConverter` and `HpInterface` use `transact()` to send over tokens with |
| * the transaction code (the first argument of `transact()`) equal to `_GHT`, |
| * which is defined as a global constant named |
| * `DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE`. |
| * |
| * In the rare occasion that this value clashes with other values already used |
| * by the `Bp` class and modifying the `Bp` class is difficult, the |
| * "GET_HAL_TOKEN" transaction code can be changed to a different value simply |
| * by replacing `DECLARE_HYBRID_META_INTERFACE` with |
| * `DECLARE_HYBRID_META_INTERFACE_WITH_CODE` in the declaration of the base |
| * interface and supplying the new transaction code in the first argument of |
| * this macro. |
| * |
| */ |
| |
| 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 BNINTERFACE> |
| class H2BConverter : public BNINTERFACE { |
| public: |
| typedef H2BConverter<HINTERFACE, BNINTERFACE> CBase; // Converter Base |
| typedef typename BNINTERFACE::BaseInterface BaseInterface; |
| typedef HINTERFACE HalInterface; |
| typedef typename BaseInterface::HalVariant HalVariant; |
| using BaseInterface::sGetHalTokenTransactionCode; |
| |
| H2BConverter(const sp<HalInterface>& base) : mBase{base} {} |
| virtual status_t onTransact(uint32_t code, |
| const Parcel& data, Parcel* reply, uint32_t flags = 0); |
| virtual status_t linkToDeath( |
| const sp<IBinder::DeathRecipient>& recipient, |
| void* cookie = nullptr, |
| uint32_t flags = 0); |
| virtual status_t unlinkToDeath( |
| const wp<IBinder::DeathRecipient>& recipient, |
| void* cookie = nullptr, |
| uint32_t flags = 0, |
| wp<IBinder::DeathRecipient>* outRecipient = nullptr); |
| virtual HalVariant getHalVariant() const override { return { mBase }; } |
| HalInterface* getBase() { return mBase.get(); } |
| |
| protected: |
| sp<HalInterface> mBase; |
| |
| private: |
| 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 <size_t Index = std::variant_size_v<HalVariant> - 1> |
| static constexpr size_t _findIndex() { |
| if constexpr (Index == 0) { |
| return Index; |
| } else if constexpr ( |
| std::is_same_v< |
| std::variant_alternative_t<Index, HalVariant>, |
| sp<HalInterface>>) { |
| return Index; |
| } else { |
| return _findIndex<Index - 1>(); |
| } |
| } |
| |
| static constexpr size_t sHalIndex = _findIndex<>(); |
| static_assert(sHalIndex != 0, |
| "H2BConverter from an unrecognized HAL interface."); |
| }; |
| |
| template <typename BPINTERFACE, typename CONVERTER, typename... CONVERTERS> |
| class HpInterface : public CONVERTER::BaseInterface { |
| public: |
| typedef HpInterface<BPINTERFACE, CONVERTER, CONVERTERS...> PBase; // Proxy Base |
| typedef typename CONVERTER::BaseInterface BaseInterface; |
| typedef typename BaseInterface::HalVariant HalVariant; |
| using BaseInterface::sGetHalTokenTransactionCode; |
| |
| explicit HpInterface(const sp<IBinder>& impl); |
| BaseInterface* getBase() { return mBase.get(); } |
| virtual HalVariant getHalVariant() const override { return mHalVariant; } |
| |
| protected: |
| IBinder* mBpBinder; |
| sp<BPINTERFACE> mBp; |
| sp<BaseInterface> mBase; |
| HalVariant mHalVariant; |
| bool mHasConverter{false}; |
| IBinder* onAsBinder() override { return mBpBinder; } |
| |
| private: |
| typedef std::variant<std::monostate, |
| CONVERTER, CONVERTERS...> _ConverterVar; |
| typedef std::variant<std::monostate, |
| typename CONVERTER::HalInterface, |
| typename CONVERTERS::HalInterface...> _ConverterHalVar; |
| typedef std::variant<std::monostate, |
| sp<typename CONVERTER::HalInterface>, |
| sp<typename CONVERTERS::HalInterface>...> _ConverterHalPointerVar; |
| |
| static_assert(std::is_same_v<_ConverterHalPointerVar, HalVariant>, |
| "Converter classes do not match HAL interfaces."); |
| |
| template <size_t Index = std::variant_size_v<HalVariant> - 1> |
| bool _castFromHalBaseAndConvert(size_t halIndex, |
| const sp<HInterface>& halBase) { |
| if constexpr (Index == 0) { |
| return false; |
| } else { |
| if (halIndex != Index) { |
| return _castFromHalBaseAndConvert<Index - 1>(halIndex, halBase); |
| } |
| typedef std::variant_alternative_t<Index, _ConverterHalVar> |
| HalInterface; |
| sp<HalInterface> halInterface = HalInterface::castFrom(halBase); |
| mHalVariant.template emplace<Index>(halInterface); |
| if (!halInterface) { |
| return false; |
| } |
| if (mHasConverter) { |
| typedef std::variant_alternative_t<Index, _ConverterVar> |
| Converter; |
| sp<Converter> converter = new Converter(halInterface); |
| if (converter) { |
| mBase = converter; |
| } else { |
| ALOGW("HpInterface: Failed to create an H2B converter -- " |
| "index = %zu.", Index); |
| } |
| } |
| return true; |
| } |
| } |
| |
| bool castFromHalBaseAndConvert(size_t halIndex, |
| const sp<HInterface>& halBase) { |
| if (!_castFromHalBaseAndConvert<>(halIndex, halBase)) { |
| return false; |
| } |
| return true; |
| } |
| |
| }; |
| |
| // ---------------------------------------------------------------------- |
| |
| #define DECLARE_HYBRID_META_INTERFACE(INTERFACE, ...) \ |
| DECLARE_HYBRID_META_INTERFACE_WITH_CODE( \ |
| ::android::DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE, \ |
| INTERFACE, __VA_ARGS__) \ |
| |
| |
| #define DECLARE_HYBRID_META_INTERFACE_WITH_CODE(GTKCODE, INTERFACE, ...) \ |
| private: \ |
| typedef ::std::variant<::std::monostate, __VA_ARGS__> _HalVariant; \ |
| template <typename... Types> \ |
| using _SpVariant = \ |
| ::std::variant<::std::monostate, ::android::sp<Types>...>; \ |
| public: \ |
| typedef _SpVariant<__VA_ARGS__> HalVariant; \ |
| virtual HalVariant getHalVariant() const; \ |
| size_t getHalIndex() const; \ |
| template <size_t Index> \ |
| using HalInterface = ::std::variant_alternative_t<Index, _HalVariant>;\ |
| template <typename HAL> \ |
| sp<HAL> getHalInterface() const { \ |
| HalVariant halVariant = getHalVariant(); \ |
| const sp<HAL>* hal = std::get_if<sp<HAL>>(&halVariant); \ |
| return hal ? *hal : nullptr; \ |
| } \ |
| \ |
| 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(); \ |
| static constexpr uint32_t sGetHalTokenTransactionCode = GTKCODE; \ |
| |
| |
| #define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, NAME) \ |
| I##INTERFACE::HalVariant I##INTERFACE::getHalVariant() const { \ |
| return HalVariant{std::in_place_index<0>}; \ |
| } \ |
| size_t I##INTERFACE::getHalIndex() const { \ |
| return getHalVariant().index(); \ |
| } \ |
| constexpr uint32_t I##INTERFACE::sGetHalTokenTransactionCode; \ |
| static const ::android::StaticString16 I##INTERFACE##_desc_str16( \ |
| u##NAME); \ |
| const ::android::String16 I##INTERFACE::descriptor( \ |
| I##INTERFACE##_desc_str16); \ |
| 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 != nullptr) { \ |
| intr = static_cast<I##INTERFACE*>( \ |
| obj->queryLocalInterface( \ |
| I##INTERFACE::descriptor).get()); \ |
| if (intr == nullptr) { \ |
| intr = new Hp##INTERFACE(obj); \ |
| } \ |
| } \ |
| return intr; \ |
| } \ |
| I##INTERFACE::I##INTERFACE() { } \ |
| I##INTERFACE::~I##INTERFACE() { } \ |
| |
| // ---------------------------------------------------------------------- |
| |
| template <typename HINTERFACE, |
| typename BNINTERFACE> |
| status_t H2BConverter<HINTERFACE, BNINTERFACE>:: |
| onTransact( |
| uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { |
| if (code == sGetHalTokenTransactionCode) { |
| if (!data.enforceInterface(BaseInterface::getInterfaceDescriptor())) { |
| return BAD_TYPE; |
| } |
| |
| HalToken token; |
| bool result; |
| result = createHalToken(mBase, &token); |
| // Write whether a HAL token is present. |
| reply->writeBool(result); |
| if (!result) { |
| ALOGE("H2BConverter: Failed to create HAL token."); |
| return NO_ERROR; |
| } |
| |
| // Write the HAL token. |
| reply->writeByteArray(token.size(), token.data()); |
| |
| // Write the HAL index. |
| reply->writeUint32(static_cast<uint32_t>(sHalIndex)); |
| |
| // Write a flag indicating that a converter needs to be created. |
| reply->writeBool(true); |
| |
| return NO_ERROR; |
| } |
| return BNINTERFACE::onTransact(code, data, reply, flags); |
| } |
| |
| template <typename HINTERFACE, |
| typename BNINTERFACE> |
| status_t H2BConverter<HINTERFACE, BNINTERFACE>::linkToDeath( |
| const sp<IBinder::DeathRecipient>& recipient, |
| void* cookie, uint32_t flags) { |
| LOG_ALWAYS_FATAL_IF( |
| recipient == nullptr, |
| "linkToDeath(): recipient must not be 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 BNINTERFACE> |
| status_t H2BConverter<HINTERFACE, BNINTERFACE>::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, typename... CONVERTERS> |
| HpInterface<BPINTERFACE, CONVERTER, CONVERTERS...>::HpInterface( |
| const sp<IBinder>& impl) |
| : mBpBinder{impl.get()}, |
| mBp{new BPINTERFACE(impl)} { |
| mBase = mBp; |
| if (!mBpBinder->remoteBinder()) { |
| return; |
| } |
| Parcel data, reply; |
| data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor()); |
| if (mBpBinder->transact(sGetHalTokenTransactionCode, |
| data, &reply) == NO_ERROR) { |
| // Read whether a HAL token is present. |
| bool tokenCreated; |
| if (reply.readBool(&tokenCreated) != OK) { |
| ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN " |
| "(tokenCreated)."); |
| } |
| |
| if (!tokenCreated) { |
| ALOGW("HpInterface: No HAL token was created."); |
| return; |
| } |
| |
| // Read the HAL token. |
| std::vector<uint8_t> tokenVector; |
| if (reply.readByteVector(&tokenVector) != OK) { |
| ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN " |
| "(halToken)."); |
| return; |
| } |
| |
| // Retrieve the HAL interface from the token. |
| HalToken token{tokenVector}; |
| sp<HInterface> halBase = retrieveHalInterface(token); |
| deleteHalToken(token); |
| |
| if (!halBase) { |
| ALOGW("HpInterface: Failed to retrieve HAL interface."); |
| return; |
| } |
| |
| uint32_t halIndex; |
| // Read the hal index. |
| if (reply.readUint32(&halIndex) != OK) { |
| ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN " |
| "(halIndex)."); |
| return; |
| } |
| |
| // Read the converter flag. |
| if (reply.readBool(&mHasConverter) != OK) { |
| ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN " |
| "(hasConverter)."); |
| return; |
| } |
| |
| // Call castFrom from the right HAL interface and create a converter if |
| // needed. |
| if (!castFromHalBaseAndConvert(static_cast<size_t>(halIndex), |
| halBase)) { |
| ALOGW("HpInterface: Failed to cast to the correct HAL interface -- " |
| "HAL index = %" PRIu32 ".", halIndex); |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| |
| } // namespace android |
| |
| #endif // ANDROID_HYBRIDINTERFACE_H |