Move hybrid interface from libbinder to here.
Also, override linkToDeath and unlinkToDeath in the wrapper class to
call linkToDeath and unlinkToDeath on the underlying HIDL interface.
Shared library: android.hidl.token@1.0-utils
Header file: hidl/HybridInterface.h
Test: With CtsMediaTestCases.apk installed,
adb shell am instrument -e size small -w
'android.media.cts/android.support.test.runner.AndroidJUnitRunner'
Bug: 33854657
Change-Id: Id66800560a67ff9f75ffd9388370541ce873e508
diff --git a/transport/Android.bp b/transport/Android.bp
index 1c1a310..f81d9c6 100644
--- a/transport/Android.bp
+++ b/transport/Android.bp
@@ -20,6 +20,7 @@
"memory/1.0",
"memory/1.0/default",
"token/1.0",
+ "token/1.0/utils",
]
cc_library_shared {
diff --git a/transport/token/1.0/utils/Android.bp b/transport/token/1.0/utils/Android.bp
new file mode 100644
index 0000000..0360d99
--- /dev/null
+++ b/transport/token/1.0/utils/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+cc_library {
+ name: "android.hidl.token@1.0-utils",
+
+ srcs: [
+ "HybridInterface.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ "liblog",
+ "libhidlbase",
+ "android.hidl.token@1.0",
+ ],
+
+ export_shared_lib_headers: [
+ "libbinder",
+ "libhidlbase",
+ ],
+
+ export_include_dirs: [
+ "include"
+ ],
+
+ clang: true,
+}
+
diff --git a/transport/token/1.0/utils/HybridInterface.cpp b/transport/token/1.0/utils/HybridInterface.cpp
new file mode 100644
index 0000000..f28446e
--- /dev/null
+++ b/transport/token/1.0/utils/HybridInterface.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "HybridInterface"
+
+#include <utils/Log.h>
+#include <hidl/HybridInterface.h>
+#include <hidl/HidlSupport.h>
+#include <android/hidl/token/1.0/ITokenManager.h>
+
+namespace android {
+
+using ::android::hidl::token::V1_0::ITokenManager;
+
+namespace {
+
+std::mutex gTokenManagerLock;
+sp<ITokenManager> gTokenManager = nullptr;
+
+struct TokenManagerDeathRecipient : public hardware::hidl_death_recipient {
+ void serviceDied(uint64_t, const wp<HInterface>&) {
+ std::lock_guard<std::mutex> lock(gTokenManagerLock);
+ gTokenManager = nullptr;
+ }
+};
+
+sp<TokenManagerDeathRecipient> gTokenManagerDeathRecipient =
+ new TokenManagerDeathRecipient();
+
+bool isBadTokenManager() {
+ if (gTokenManager != nullptr) {
+ return false;
+ }
+ gTokenManager = ITokenManager::getService();
+ if (gTokenManager == nullptr) {
+ ALOGE("Cannot retrieve TokenManager.");
+ return true;
+ }
+ auto transaction = gTokenManager->linkToDeath(
+ gTokenManagerDeathRecipient, 0);
+ if (!transaction.isOk()) {
+ ALOGE("Cannot observe TokenManager's death.");
+ gTokenManager = nullptr;
+ return true;
+ }
+ return false;
+}
+
+template <typename ReturnType>
+bool isBadTransaction(hardware::Return<ReturnType>& transaction) {
+ if (transaction.isOk()) {
+ return false;
+ }
+ ALOGE("TokenManager's transaction error: %s",
+ transaction.description().c_str());
+ gTokenManager->unlinkToDeath(gTokenManagerDeathRecipient).isOk();
+ gTokenManager = nullptr;
+ return true;
+}
+
+} // unnamed namespace
+
+sp<HInterface> retrieveHalInterface(const HalToken& token) {
+ hardware::Return<sp<HInterface> > transaction(nullptr);
+ {
+ std::lock_guard<std::mutex> lock(gTokenManagerLock);
+ if (isBadTokenManager()) {
+ return nullptr;
+ }
+ transaction = gTokenManager->get(token);
+ if (isBadTransaction(transaction)) {
+ return nullptr;
+ }
+ }
+ return static_cast<sp<HInterface> >(transaction);
+}
+
+bool createHalToken(const sp<HInterface>& interface, HalToken* token) {
+ hardware::Return<HalToken> transaction(false);
+ {
+ std::lock_guard<std::mutex> lock(gTokenManagerLock);
+ if (isBadTokenManager()) {
+ return false;
+ }
+ transaction = gTokenManager->createToken(interface);
+ if (isBadTransaction(transaction)) {
+ return false;
+ }
+ }
+ *token = static_cast<HalToken>(transaction);
+ return true;
+}
+
+bool deleteHalToken(const HalToken& token) {
+ hardware::Return<bool> transaction(false);
+ {
+ std::lock_guard<std::mutex> lock(gTokenManagerLock);
+ if (isBadTokenManager()) {
+ return false;
+ }
+ transaction = gTokenManager->unregister(token);
+ if (isBadTransaction(transaction)) {
+ return false;
+ }
+ }
+ return static_cast<bool>(transaction);
+}
+
+}; // namespace android
+
diff --git a/transport/token/1.0/utils/include/hidl/HybridInterface.h b/transport/token/1.0/utils/include/hidl/HybridInterface.h
new file mode 100644
index 0000000..42d3734
--- /dev/null
+++ b/transport/token/1.0/utils/include/hidl/HybridInterface.h
@@ -0,0 +1,338 @@
+/*
+ * 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 uint64_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->writeUint64(token);
+ 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();
+ HalToken token = reply.readUint64();
+ 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