1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef ANDROID_HYBRIDINTERFACE_H
18 #define ANDROID_HYBRIDINTERFACE_H
19
20 #include <vector>
21 #include <mutex>
22
23 #include <binder/Parcel.h>
24 #include <hidl/HidlSupport.h>
25
26 #include <cinttypes>
27 #include <variant>
28
29 /**
30 * Hybrid Interfaces
31 * =================
32 *
33 * A hybrid interface is a binder interface that
34 * 1. is implemented both traditionally and as a wrapper around a hidl
35 * interface, and allows querying whether the underlying instance comes from
36 * a hidl interface or not; and
37 * 2. allows efficient calls to a hidl interface (if the underlying instance
38 * comes from a hidl interface) by automatically creating the wrapper in the
39 * process that calls it.
40 *
41 * Terminology:
42 * - `HalToken`: The type for a "token" of a hidl interface. This is defined to
43 * be compatible with `ITokenManager.hal`.
44 * - `HInterface`: The base type for a hidl interface. Currently, it is defined
45 * as `::android::hidl::base::V1_0::IBase`.
46 * - `HALINTERFACE`: The hidl interface that will be sent through binders.
47 * - `INTERFACE`: The binder interface that will be the wrapper of
48 * `HALINTERFACE`. `INTERFACE` is supposed to be somewhat similar to
49 * `HALINTERFACE`.
50 *
51 * To demonstrate how this is done, here is an example. Suppose `INTERFACE` is
52 * `IFoo` and `HALINTERFACE` is `HFoo`. The required steps are:
53 * 1. Use `DECLARE_HYBRID_META_INTERFACE` instead of `DECLARE_META_INTERFACE` in
54 * the declaration of `IFoo`. `DECLARE_HYBRID_META_INTERFACE` takes an
55 * additional argument that is the hidl interface to be converted into a
56 * binder interface. Example:
57 * Change from `DECLARE_META_INTERFACE(Foo)`
58 * to `DECLARE_HYBRID_META_INTERFACE(Foo, HFoo)`
59 * 2. Create a converter class that derives from
60 * `H2BConverter<HFoo, BnFoo>`. Let us call this `H2BFoo`.
61 * 3. Add the following constructor in `H2BFoo` that call the corresponding
62 * constructors in `H2BConverter`:
63 * `H2BFoo(const sp<HalInterface>& base) : CBase(base) {}`
64 * Note: `CBase = H2BConverter<HFoo, BnFoo>` and `HalInterface = HFoo` are
65 * member typedefs of `H2BConverter<HFoo, BnFoo>`, so the above line can be
66 * copied verbatim into `H2BFoo`.
67 * 4. Implement `IFoo` in `H2BFoo` on top of `HFoo`. `H2BConverter` provides a
68 * protected `mBase` of type `sp<HFoo>` that can be used to access the `HFoo`
69 * instance. (There is also a public function named `getBase()` that returns
70 * `mBase`.)
71 * 5. Create a hardware proxy class that derives from
72 * `HpInterface<BpFoo, H2BFoo>`. Name this class `HpFoo`. (This name cannot
73 * deviate. See step 8 below.)
74 * 6. Add the following constructor to `HpFoo`:
75 * `HpFoo(const sp<IBinder>& base): PBase(base) {}`
76 * Note: `PBase` a member typedef of `HpInterface<BpFoo, H2BFoo>` that is
77 * equal to `HpInterface<BpFoo, H2BFoo>` itself, so the above line can be
78 * copied verbatim into `HpFoo`.
79 * 7. Delegate all functions in `HpFoo` that come from `IFoo` (except those that
80 * are defined by the macro `DECLARE_HYBRID_META_INTERFACE`) to the protected
81 * member `mBase`. `mBase` is defined in `HpInterface<BpFoo, H2BFoo>` (hence
82 * in `HpFoo`) with type `IFoo`. There is also a public function named
83 * `getBase()` that returns `mBase`.
84 * 8. Replace the existing `IMPLEMENT_META_INTERFACE` for `IFoo` by
85 * `IMPLEMENT_HYBRID_META_INTERFACE`. This macro assumes that the subclass of
86 * `HpInterface` for `IFoo` is named `HpFoo`.
87 *
88 * After the hybrid interface has been put in place properly, it can be used to
89 * do the following tasks:
90 * 1. Create an `IFoo` instance from an `HFoo` by passing `sp<HFoo>` to
91 * the constructor of `H2BFoo`.
92 * 2. Retrieve an `HFoo` from an `HpFoo` instance by calling
93 * `HpFoo::getHalInterface<HFoo>()`. This function may return `nullptr` if
94 * the `HpFoo` object is not backed by `HFoo`. The template parameter is
95 * required because `HpFoo` in fact may be backed by multiple H2B converter
96 * classes.
97 *
98 * Multiple H2B Converters
99 * =======================
100 *
101 * Because the system may support multiple versions of hidl interfaces for the
102 * same object, one binder interface may correspond to multiple H2B converters.
103 * The hybrid interface is designed to handle this as
104 * well---`DECLARE_HYBRID_META_INTERFACE` and `HpInterface` can take a variable
105 * number of arguments.
106 *
107 * As a concrete example, suppose `IFoo` is a binder interface that corresponds
108 * to two hidl interfaces `HFoo1` and `HFoo2`. That means `HpFoo`, the hybrid
109 * interface presenting `IFoo`, may be backed by `HFoo1` or `HFoo2`. This is
110 * achievable by
111 *
112 * - Replacing `DECLARE_META_INTERFACE(Foo)` by
113 * `DECLARE_HYBRID_META_INTERFACE(Foo, HFoo1, HFoo2)` in the declaration of
114 * `IFoo`.
115 * - Creating `H2BFoo1` as a subclass of `H2BConverter<HFoo1, BnFoo>`;
116 * - Creating `H2BFoo2` as a subclass of `H2BConverter<HFoo2, BnFoo>`; and
117 * - Creating `HpFoo` as a subclass of `HpInterface<BpFoo, H2BFoo1, H2BFoo2>`.
118 *
119 * It is important that `HFoo1` and `HFoo2` are different hidl interfaces. [The
120 * actual requirement is that for each pair `<HFoo, IFoo>`, there can be only
121 * one subclass of `H2BConverter<HFoo, BnFoo>`.]
122 *
123 * As mentioned in the previous section, `HpFoo::getHalInterface` requires a
124 * template argument because it must be able to return different hidl
125 * interface types based on which hidl interface is being used. The user of
126 * `HpFoo` can query the type of the underlying hidl interface by calling
127 * `HpFoo::getHalIndex()`. The return value is a 1-based index into the list of
128 * all the supported hidl interfaces. In the example with 2 hidl interfaces
129 * `HFoo1` and `HFoo2`, index 1 corresponds to `HFoo1` and index 2 corresponds
130 * to `HFoo2`. A typical code block that accesses the underlying hidl interface
131 * of would look like this:
132 *
133 * void someFunction(const sp<IFoo> &foo) {
134 *
135 * switch (foo->getHalIndex()) {
136 * case 1: {
137 * sp<HFoo1> hFoo1 = foo->getHalInterface<HFoo1>();
138 * ...
139 * break;
140 * }
141 * case 2: {
142 * sp<HFoo2> hFoo2 = foo->getHalInterface<HFoo2>();
143 * ...
144 * break;
145 * }
146 * default: // Not backed by a hidl interface.
147 * // Alternatively, "case 0:" can be used.
148 * }
149 *
150 * }
151 *
152 * Error State
153 * ===========
154 *
155 * A corrupted transaction may cause an `HpInterface` to be in an error state.
156 * This could cause `getHalInterface<ExpectedHalInterface>()` to return
157 * `nullptr` even though `getHalIndex()` returns a non-zero index and
158 * `ExpectedHalInterface` is the corresponding hidl interface. It is therefore
159 * recommended that a null check be performed on the return value of
160 * `getHalInterface` before using it.
161 *
162 * DECLARE_HYBRID_META_INTERFACE_WITH_CODE
163 * =======================================
164 *
165 * `H2BConverter` and `HpInterface` use `transact()` to send over tokens with
166 * the transaction code (the first argument of `transact()`) equal to `_GHT`,
167 * which is defined as a global constant named
168 * `DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE`.
169 *
170 * In the rare occasion that this value clashes with other values already used
171 * by the `Bp` class and modifying the `Bp` class is difficult, the
172 * "GET_HAL_TOKEN" transaction code can be changed to a different value simply
173 * by replacing `DECLARE_HYBRID_META_INTERFACE` with
174 * `DECLARE_HYBRID_META_INTERFACE_WITH_CODE` in the declaration of the base
175 * interface and supplying the new transaction code in the first argument of
176 * this macro.
177 *
178 */
179
180 namespace android {
181
182 typedef ::android::hardware::hidl_vec<uint8_t> HalToken;
183 typedef ::android::hidl::base::V1_0::IBase HInterface;
184
185 constexpr uint32_t DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE =
186 B_PACK_CHARS('_', 'G', 'H', 'T');
187
188 sp<HInterface> retrieveHalInterface(const HalToken& token);
189 bool createHalToken(const sp<HInterface>& interface, HalToken* token);
190 bool deleteHalToken(const HalToken& token);
191
192 template <typename HINTERFACE,
193 typename BNINTERFACE>
194 class H2BConverter : public BNINTERFACE {
195 public:
196 typedef H2BConverter<HINTERFACE, BNINTERFACE> CBase; // Converter Base
197 typedef typename BNINTERFACE::BaseInterface BaseInterface;
198 typedef HINTERFACE HalInterface;
199 typedef typename BaseInterface::HalVariant HalVariant;
200 using BaseInterface::sGetHalTokenTransactionCode;
201
H2BConverter(const sp<HalInterface> & base)202 H2BConverter(const sp<HalInterface>& base) : mBase{base} {}
203 virtual status_t onTransact(uint32_t code,
204 const Parcel& data, Parcel* reply, uint32_t flags = 0);
205 virtual status_t linkToDeath(
206 const sp<IBinder::DeathRecipient>& recipient,
207 void* cookie = nullptr,
208 uint32_t flags = 0);
209 virtual status_t unlinkToDeath(
210 const wp<IBinder::DeathRecipient>& recipient,
211 void* cookie = nullptr,
212 uint32_t flags = 0,
213 wp<IBinder::DeathRecipient>* outRecipient = nullptr);
getHalVariant()214 virtual HalVariant getHalVariant() const override { return { mBase }; }
getBase()215 HalInterface* getBase() { return mBase.get(); }
216
217 protected:
218 sp<HalInterface> mBase;
219
220 private:
221 struct Obituary : public hardware::hidl_death_recipient {
222 wp<IBinder::DeathRecipient> recipient;
223 void* cookie;
224 uint32_t flags;
225 wp<IBinder> who;
ObituaryObituary226 Obituary(
227 const wp<IBinder::DeathRecipient>& r,
228 void* c, uint32_t f,
229 const wp<IBinder>& w) :
230 recipient(r), cookie(c), flags(f), who(w) {
231 }
ObituaryObituary232 Obituary(const Obituary& o) :
233 recipient(o.recipient),
234 cookie(o.cookie),
235 flags(o.flags),
236 who(o.who) {
237 }
238 Obituary& operator=(const Obituary& o) {
239 recipient = o.recipient;
240 cookie = o.cookie;
241 flags = o.flags;
242 who = o.who;
243 return *this;
244 }
serviceDiedObituary245 void serviceDied(uint64_t, const wp<HInterface>&) override {
246 sp<IBinder::DeathRecipient> dr = recipient.promote();
247 if (dr != nullptr) {
248 dr->binderDied(who);
249 }
250 }
251 };
252 std::mutex mObituariesLock;
253 std::vector<sp<Obituary> > mObituaries;
254
255 template <size_t Index = std::variant_size_v<HalVariant> - 1>
_findIndex()256 static constexpr size_t _findIndex() {
257 if constexpr (Index == 0) {
258 return Index;
259 } else if constexpr (
260 std::is_same_v<
261 std::variant_alternative_t<Index, HalVariant>,
262 sp<HalInterface>>) {
263 return Index;
264 } else {
265 return _findIndex<Index - 1>();
266 }
267 }
268
269 static constexpr size_t sHalIndex = _findIndex<>();
270 static_assert(sHalIndex != 0,
271 "H2BConverter from an unrecognized HAL interface.");
272 };
273
274 template <typename BPINTERFACE, typename CONVERTER, typename... CONVERTERS>
275 class HpInterface : public CONVERTER::BaseInterface {
276 public:
277 typedef HpInterface<BPINTERFACE, CONVERTER, CONVERTERS...> PBase; // Proxy Base
278 typedef typename CONVERTER::BaseInterface BaseInterface;
279 typedef typename BaseInterface::HalVariant HalVariant;
280 using BaseInterface::sGetHalTokenTransactionCode;
281
282 explicit HpInterface(const sp<IBinder>& impl);
getBase()283 BaseInterface* getBase() { return mBase.get(); }
getHalVariant()284 virtual HalVariant getHalVariant() const override { return mHalVariant; }
285
286 protected:
287 IBinder* mBpBinder;
288 sp<BPINTERFACE> mBp;
289 sp<BaseInterface> mBase;
290 HalVariant mHalVariant;
291 bool mHasConverter{false};
onAsBinder()292 IBinder* onAsBinder() override { return mBpBinder; }
293
294 private:
295 typedef std::variant<std::monostate,
296 CONVERTER, CONVERTERS...> _ConverterVar;
297 typedef std::variant<std::monostate,
298 typename CONVERTER::HalInterface,
299 typename CONVERTERS::HalInterface...> _ConverterHalVar;
300 typedef std::variant<std::monostate,
301 sp<typename CONVERTER::HalInterface>,
302 sp<typename CONVERTERS::HalInterface>...> _ConverterHalPointerVar;
303
304 static_assert(std::is_same_v<_ConverterHalPointerVar, HalVariant>,
305 "Converter classes do not match HAL interfaces.");
306
307 template <size_t Index = std::variant_size_v<HalVariant> - 1>
_castFromHalBaseAndConvert(size_t halIndex,const sp<HInterface> & halBase)308 bool _castFromHalBaseAndConvert(size_t halIndex,
309 const sp<HInterface>& halBase) {
310 if constexpr (Index == 0) {
311 return false;
312 } else {
313 if (halIndex != Index) {
314 return _castFromHalBaseAndConvert<Index - 1>(halIndex, halBase);
315 }
316 typedef std::variant_alternative_t<Index, _ConverterHalVar>
317 HalInterface;
318 sp<HalInterface> halInterface = HalInterface::castFrom(halBase);
319 mHalVariant.template emplace<Index>(halInterface);
320 if (!halInterface) {
321 return false;
322 }
323 if (mHasConverter) {
324 typedef std::variant_alternative_t<Index, _ConverterVar>
325 Converter;
326 sp<Converter> converter = new Converter(halInterface);
327 if (converter) {
328 mBase = converter;
329 } else {
330 ALOGW("HpInterface: Failed to create an H2B converter -- "
331 "index = %zu.", Index);
332 }
333 }
334 return true;
335 }
336 }
337
castFromHalBaseAndConvert(size_t halIndex,const sp<HInterface> & halBase)338 bool castFromHalBaseAndConvert(size_t halIndex,
339 const sp<HInterface>& halBase) {
340 if (!_castFromHalBaseAndConvert<>(halIndex, halBase)) {
341 return false;
342 }
343 return true;
344 }
345
346 };
347
348 // ----------------------------------------------------------------------
349
350 #define DECLARE_HYBRID_META_INTERFACE(INTERFACE, ...) \
351 DECLARE_HYBRID_META_INTERFACE_WITH_CODE( \
352 ::android::DEFAULT_GET_HAL_TOKEN_TRANSACTION_CODE, \
353 INTERFACE, __VA_ARGS__) \
354
355
356 #define DECLARE_HYBRID_META_INTERFACE_WITH_CODE(GTKCODE, INTERFACE, ...) \
357 private: \
358 typedef ::std::variant<::std::monostate, __VA_ARGS__> _HalVariant; \
359 template <typename... Types> \
360 using _SpVariant = \
361 ::std::variant<::std::monostate, ::android::sp<Types>...>; \
362 public: \
363 typedef _SpVariant<__VA_ARGS__> HalVariant; \
364 virtual HalVariant getHalVariant() const; \
365 size_t getHalIndex() const; \
366 template <size_t Index> \
367 using HalInterface = ::std::variant_alternative_t<Index, _HalVariant>;\
368 template <typename HAL> \
369 sp<HAL> getHalInterface() const { \
370 HalVariant halVariant = getHalVariant(); \
371 const sp<HAL>* hal = std::get_if<sp<HAL>>(&halVariant); \
372 return hal ? *hal : nullptr; \
373 } \
374 \
375 static const ::android::String16 descriptor; \
376 static ::android::sp<I##INTERFACE> asInterface( \
377 const ::android::sp<::android::IBinder>& obj); \
378 virtual const ::android::String16& getInterfaceDescriptor() const; \
379 I##INTERFACE(); \
380 virtual ~I##INTERFACE(); \
381 static constexpr uint32_t sGetHalTokenTransactionCode = GTKCODE; \
382
383
384 #define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, NAME) \
385 I##INTERFACE::HalVariant I##INTERFACE::getHalVariant() const { \
386 return HalVariant{std::in_place_index<0>}; \
387 } \
388 size_t I##INTERFACE::getHalIndex() const { \
389 return getHalVariant().index(); \
390 } \
391 constexpr uint32_t I##INTERFACE::sGetHalTokenTransactionCode; \
392 static const ::android::StaticString16 I##INTERFACE##_desc_str16( \
393 u##NAME); \
394 const ::android::String16 I##INTERFACE::descriptor( \
395 I##INTERFACE##_desc_str16); \
396 const ::android::String16& \
397 I##INTERFACE::getInterfaceDescriptor() const { \
398 return I##INTERFACE::descriptor; \
399 } \
400 ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
401 const ::android::sp<::android::IBinder>& obj) \
402 { \
403 ::android::sp<I##INTERFACE> intr; \
404 if (obj != nullptr) { \
405 intr = static_cast<I##INTERFACE*>( \
406 obj->queryLocalInterface( \
407 I##INTERFACE::descriptor).get()); \
408 if (intr == nullptr) { \
409 intr = new Hp##INTERFACE(obj); \
410 } \
411 } \
412 return intr; \
413 } \
414 I##INTERFACE::I##INTERFACE() { } \
415 I##INTERFACE::~I##INTERFACE() { } \
416
417 // ----------------------------------------------------------------------
418
419 template <typename HINTERFACE,
420 typename BNINTERFACE>
421 status_t H2BConverter<HINTERFACE, BNINTERFACE>::
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)422 onTransact(
423 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
424 if (code == sGetHalTokenTransactionCode) {
425 if (!data.enforceInterface(BaseInterface::getInterfaceDescriptor())) {
426 return BAD_TYPE;
427 }
428
429 HalToken token;
430 bool result;
431 result = createHalToken(mBase, &token);
432 // Write whether a HAL token is present.
433 reply->writeBool(result);
434 if (!result) {
435 ALOGE("H2BConverter: Failed to create HAL token.");
436 return NO_ERROR;
437 }
438
439 // Write the HAL token.
440 reply->writeByteArray(token.size(), token.data());
441
442 // Write the HAL index.
443 reply->writeUint32(static_cast<uint32_t>(sHalIndex));
444
445 // Write a flag indicating that a converter needs to be created.
446 reply->writeBool(true);
447
448 return NO_ERROR;
449 }
450 return BNINTERFACE::onTransact(code, data, reply, flags);
451 }
452
453 template <typename HINTERFACE,
454 typename BNINTERFACE>
linkToDeath(const sp<IBinder::DeathRecipient> & recipient,void * cookie,uint32_t flags)455 status_t H2BConverter<HINTERFACE, BNINTERFACE>::linkToDeath(
456 const sp<IBinder::DeathRecipient>& recipient,
457 void* cookie, uint32_t flags) {
458 LOG_ALWAYS_FATAL_IF(
459 recipient == nullptr,
460 "linkToDeath(): recipient must not be null.");
461 {
462 std::lock_guard<std::mutex> lock(mObituariesLock);
463 mObituaries.push_back(new Obituary(recipient, cookie, flags, this));
464 if (!mBase->linkToDeath(mObituaries.back(), 0)) {
465 return DEAD_OBJECT;
466 }
467 }
468 return NO_ERROR;
469 }
470
471 template <typename HINTERFACE,
472 typename BNINTERFACE>
unlinkToDeath(const wp<IBinder::DeathRecipient> & recipient,void * cookie,uint32_t flags,wp<IBinder::DeathRecipient> * outRecipient)473 status_t H2BConverter<HINTERFACE, BNINTERFACE>::unlinkToDeath(
474 const wp<IBinder::DeathRecipient>& recipient,
475 void* cookie, uint32_t flags,
476 wp<IBinder::DeathRecipient>* outRecipient) {
477 std::lock_guard<std::mutex> lock(mObituariesLock);
478 for (auto i = mObituaries.begin(); i != mObituaries.end(); ++i) {
479 if ((flags = (*i)->flags) && (
480 (recipient == (*i)->recipient) ||
481 ((recipient == nullptr) && (cookie == (*i)->cookie)))) {
482 if (outRecipient != nullptr) {
483 *outRecipient = (*i)->recipient;
484 }
485 bool success = mBase->unlinkToDeath(*i);
486 mObituaries.erase(i);
487 return success ? NO_ERROR : DEAD_OBJECT;
488 }
489 }
490 return NAME_NOT_FOUND;
491 }
492
493 template <typename BPINTERFACE, typename CONVERTER, typename... CONVERTERS>
HpInterface(const sp<IBinder> & impl)494 HpInterface<BPINTERFACE, CONVERTER, CONVERTERS...>::HpInterface(
495 const sp<IBinder>& impl)
496 : mBpBinder{impl.get()},
497 mBp{new BPINTERFACE(impl)} {
498 mBase = mBp;
499 if (!mBpBinder->remoteBinder()) {
500 return;
501 }
502 Parcel data, reply;
503 data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor());
504 if (mBpBinder->transact(sGetHalTokenTransactionCode,
505 data, &reply) == NO_ERROR) {
506 // Read whether a HAL token is present.
507 bool tokenCreated;
508 if (reply.readBool(&tokenCreated) != OK) {
509 ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN "
510 "(tokenCreated).");
511 }
512
513 if (!tokenCreated) {
514 ALOGW("HpInterface: No HAL token was created.");
515 return;
516 }
517
518 // Read the HAL token.
519 std::vector<uint8_t> tokenVector;
520 if (reply.readByteVector(&tokenVector) != OK) {
521 ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN "
522 "(halToken).");
523 return;
524 }
525
526 // Retrieve the HAL interface from the token.
527 HalToken token{tokenVector};
528 sp<HInterface> halBase = retrieveHalInterface(token);
529 deleteHalToken(token);
530
531 if (!halBase) {
532 ALOGW("HpInterface: Failed to retrieve HAL interface.");
533 return;
534 }
535
536 uint32_t halIndex;
537 // Read the hal index.
538 if (reply.readUint32(&halIndex) != OK) {
539 ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN "
540 "(halIndex).");
541 return;
542 }
543
544 // Read the converter flag.
545 if (reply.readBool(&mHasConverter) != OK) {
546 ALOGW("HpInterface: Corrupted parcel from GET_HAL_TOKEN "
547 "(hasConverter).");
548 return;
549 }
550
551 // Call castFrom from the right HAL interface and create a converter if
552 // needed.
553 if (!castFromHalBaseAndConvert(static_cast<size_t>(halIndex),
554 halBase)) {
555 ALOGW("HpInterface: Failed to cast to the correct HAL interface -- "
556 "HAL index = %" PRIu32 ".", halIndex);
557 }
558 }
559 }
560
561 // ----------------------------------------------------------------------
562
563 } // namespace android
564
565 #endif // ANDROID_HYBRIDINTERFACE_H
566