SafeInterface.h revision 2537db72f62d53d7c7cab85ccbba2e8d0286a2da
1/*
2 * Copyright 2016 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#pragma once
18
19#include <binder/IInterface.h>
20#include <binder/Parcel.h>
21#include <cutils/compiler.h>
22
23// Set to 1 to enable CallStacks when logging errors
24#define SI_DUMP_CALLSTACKS 0
25#if SI_DUMP_CALLSTACKS
26#include <utils/CallStack.h>
27#endif
28
29#include <utils/NativeHandle.h>
30
31#include <functional>
32#include <type_traits>
33
34namespace android {
35namespace SafeInterface {
36
37// ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way
38class ParcelHandler {
39public:
40    explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {}
41
42    // Specializations for types with dedicated handling in Parcel
43    status_t read(const Parcel& parcel, bool* b) const {
44        return callParcel("readBool", [&]() { return parcel.readBool(b); });
45    }
46    status_t write(Parcel* parcel, bool b) const {
47        return callParcel("writeBool", [&]() { return parcel->writeBool(b); });
48    }
49    template <typename E>
50    typename std::enable_if<std::is_enum<E>::value, status_t>::type read(const Parcel& parcel,
51                                                                         E* e) const {
52        typename std::underlying_type<E>::type u{};
53        status_t result = read(parcel, &u);
54        *e = static_cast<E>(u);
55        return result;
56    }
57    template <typename E>
58    typename std::enable_if<std::is_enum<E>::value, status_t>::type write(Parcel* parcel,
59                                                                          E e) const {
60        return write(parcel, static_cast<typename std::underlying_type<E>::type>(e));
61    }
62    template <typename T>
63    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
64            const Parcel& parcel, T* t) const {
65        return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); });
66    }
67    template <typename T>
68    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
69            Parcel* parcel, const T& t) const {
70        return callParcel("write(Flattenable)", [&]() { return parcel->write(t); });
71    }
72    template <typename T>
73    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
74            const Parcel& parcel, sp<T>* t) const {
75        *t = new T{};
76        return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
77    }
78    template <typename T>
79    typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type write(
80            Parcel* parcel, const sp<T>& t) const {
81        return callParcel("write(sp<Flattenable>)", [&]() { return parcel->write(*(t.get())); });
82    }
83    template <typename T>
84    typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type read(
85            const Parcel& parcel, T* t) const {
86        return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); });
87    }
88    template <typename T>
89    typename std::enable_if<std::is_base_of<LightFlattenable<T>, T>::value, status_t>::type write(
90            Parcel* parcel, const T& t) const {
91        return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); });
92    }
93    template <typename NH>
94    typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type read(
95            const Parcel& parcel, NH* nh) {
96        *nh = NativeHandle::create(parcel.readNativeHandle(), true);
97        return NO_ERROR;
98    }
99    template <typename NH>
100    typename std::enable_if<std::is_same<NH, sp<NativeHandle>>::value, status_t>::type write(
101            Parcel* parcel, const NH& nh) {
102        return callParcel("write(sp<NativeHandle>)",
103                          [&]() { return parcel->writeNativeHandle(nh->handle()); });
104    }
105    template <typename T>
106    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type read(
107            const Parcel& parcel, T* t) const {
108        return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); });
109    }
110    template <typename T>
111    typename std::enable_if<std::is_base_of<Parcelable, T>::value, status_t>::type write(
112            Parcel* parcel, const T& t) const {
113        return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); });
114    }
115    status_t read(const Parcel& parcel, String8* str) const {
116        return callParcel("readString8", [&]() { return parcel.readString8(str); });
117    }
118    status_t write(Parcel* parcel, const String8& str) const {
119        return callParcel("writeString8", [&]() { return parcel->writeString8(str); });
120    }
121    template <typename T>
122    typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type read(
123            const Parcel& parcel, sp<T>* pointer) const {
124        return callParcel("readNullableStrongBinder",
125                          [&]() { return parcel.readNullableStrongBinder(pointer); });
126    }
127    template <typename T>
128    typename std::enable_if<std::is_same<IBinder, T>::value, status_t>::type write(
129            Parcel* parcel, const sp<T>& pointer) const {
130        return callParcel("writeStrongBinder",
131                          [&]() { return parcel->writeStrongBinder(pointer); });
132    }
133    template <typename T>
134    typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type read(
135            const Parcel& parcel, sp<T>* pointer) const {
136        return callParcel("readNullableStrongBinder[IInterface]",
137                          [&]() { return parcel.readNullableStrongBinder(pointer); });
138    }
139    template <typename T>
140    typename std::enable_if<std::is_base_of<IInterface, T>::value, status_t>::type write(
141            Parcel* parcel, const sp<T>& interface) const {
142        return write(parcel, IInterface::asBinder(interface));
143    }
144
145    // Templates to handle integral types. We use a struct template to require that the called
146    // function exactly matches the signedness and size of the argument (e.g., the argument isn't
147    // silently widened).
148    template <bool isSigned, size_t size, typename I>
149    struct HandleInt;
150    template <typename I>
151    struct HandleInt<true, 4, I> {
152        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
153            return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); });
154        }
155        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
156            return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); });
157        }
158    };
159    template <typename I>
160    struct HandleInt<false, 4, I> {
161        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
162            return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); });
163        }
164        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
165            return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); });
166        }
167    };
168    template <typename I>
169    struct HandleInt<true, 8, I> {
170        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
171            return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); });
172        }
173        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
174            return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); });
175        }
176    };
177    template <typename I>
178    struct HandleInt<false, 8, I> {
179        static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) {
180            return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); });
181        }
182        static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) {
183            return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); });
184        }
185    };
186    template <typename I>
187    typename std::enable_if<std::is_integral<I>::value, status_t>::type read(const Parcel& parcel,
188                                                                             I* i) const {
189        return HandleInt<std::is_signed<I>::value, sizeof(I), I>::read(*this, parcel, i);
190    }
191    template <typename I>
192    typename std::enable_if<std::is_integral<I>::value, status_t>::type write(Parcel* parcel,
193                                                                              I i) const {
194        return HandleInt<std::is_signed<I>::value, sizeof(I), I>::write(*this, parcel, i);
195    }
196
197private:
198    const char* const mLogTag;
199
200    // Helper to encapsulate error handling while calling the various Parcel methods
201    template <typename Function>
202    status_t callParcel(const char* name, Function f) const {
203        status_t error = f();
204        if (CC_UNLIKELY(error != NO_ERROR)) {
205            ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error));
206#if SI_DUMP_CALLSTACKS
207            CallStack callStack(mLogTag);
208#endif
209        }
210        return error;
211    }
212};
213
214// Utility struct template which allows us to retrieve the types of the parameters of a member
215// function pointer
216template <typename T>
217struct ParamExtractor;
218template <typename Class, typename Return, typename... Params>
219struct ParamExtractor<Return (Class::*)(Params...)> {
220    using ParamTuple = std::tuple<Params...>;
221};
222template <typename Class, typename Return, typename... Params>
223struct ParamExtractor<Return (Class::*)(Params...) const> {
224    using ParamTuple = std::tuple<Params...>;
225};
226
227} // namespace SafeInterface
228
229template <typename Interface>
230class SafeBpInterface : public BpInterface<Interface> {
231protected:
232    SafeBpInterface(const sp<IBinder>& impl, const char* logTag)
233          : BpInterface<Interface>(impl), mLogTag(logTag) {}
234    ~SafeBpInterface() override = default;
235
236    // callRemote is used to invoke a synchronous procedure call over Binder
237    template <typename Method, typename TagType, typename... Args>
238    status_t callRemote(TagType tag, Args&&... args) const {
239        static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
240
241        // Verify that the arguments are compatible with the parameters
242        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
243        static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
244                      "Invalid argument type");
245
246        // Write the input arguments to the data Parcel
247        Parcel data;
248        data.writeInterfaceToken(this->getInterfaceDescriptor());
249
250        status_t error = writeInputs(&data, std::forward<Args>(args)...);
251        if (CC_UNLIKELY(error != NO_ERROR)) {
252            // A message will have been logged by writeInputs
253            return error;
254        }
255
256        // Send the data Parcel to the remote and retrieve the reply parcel
257        Parcel reply;
258        error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply);
259        if (CC_UNLIKELY(error != NO_ERROR)) {
260            ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
261#if SI_DUMP_CALLSTACKS
262            CallStack callStack(mLogTag);
263#endif
264            return error;
265        }
266
267        // Read the outputs from the reply Parcel into the output arguments
268        error = readOutputs(reply, std::forward<Args>(args)...);
269        if (CC_UNLIKELY(error != NO_ERROR)) {
270            // A message will have been logged by readOutputs
271            return error;
272        }
273
274        // Retrieve the result code from the reply Parcel
275        status_t result = NO_ERROR;
276        error = reply.readInt32(&result);
277        if (CC_UNLIKELY(error != NO_ERROR)) {
278            ALOG(LOG_ERROR, mLogTag, "Failed to obtain result");
279#if SI_DUMP_CALLSTACKS
280            CallStack callStack(mLogTag);
281#endif
282            return error;
283        }
284        return result;
285    }
286
287    // callRemoteAsync is used to invoke an asynchronous procedure call over Binder
288    template <typename Method, typename TagType, typename... Args>
289    void callRemoteAsync(TagType tag, Args&&... args) const {
290        static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t");
291
292        // Verify that the arguments are compatible with the parameters
293        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
294        static_assert(ArgsMatchParams<std::tuple<Args...>, ParamTuple>::value,
295                      "Invalid argument type");
296
297        // Write the input arguments to the data Parcel
298        Parcel data;
299        data.writeInterfaceToken(this->getInterfaceDescriptor());
300        status_t error = writeInputs(&data, std::forward<Args>(args)...);
301        if (CC_UNLIKELY(error != NO_ERROR)) {
302            // A message will have been logged by writeInputs
303            return;
304        }
305
306        // There will be no data in the reply Parcel since the call is one-way
307        Parcel reply;
308        error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply,
309                                         IBinder::FLAG_ONEWAY);
310        if (CC_UNLIKELY(error != NO_ERROR)) {
311            ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
312#if SI_DUMP_CALLSTACKS
313            CallStack callStack(mLogTag);
314#endif
315        }
316    }
317
318private:
319    const char* const mLogTag;
320
321    // This struct provides information on whether the decayed types of the elements at Index in the
322    // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references,
323    // and a few other less common operations) are the same
324    template <size_t Index, typename T, typename U>
325    struct DecayedElementsMatch {
326    private:
327        using FirstT = typename std::tuple_element<Index, T>::type;
328        using DecayedT = typename std::decay<FirstT>::type;
329        using FirstU = typename std::tuple_element<Index, U>::type;
330        using DecayedU = typename std::decay<FirstU>::type;
331
332    public:
333        static constexpr bool value = std::is_same<DecayedT, DecayedU>::value;
334    };
335
336    // When comparing whether the argument types match the parameter types, we first decay them (see
337    // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are
338    // equivalent enough for our purposes
339    template <typename T, typename U>
340    struct ArgsMatchParams {};
341    template <typename... Args, typename... Params>
342    struct ArgsMatchParams<std::tuple<Args...>, std::tuple<Params...>> {
343        static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments");
344        static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments");
345
346    private:
347        template <size_t Index>
348        static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type
349        elementsMatch() {
350            if (!DecayedElementsMatch<Index, std::tuple<Args...>, std::tuple<Params...>>::value) {
351                return false;
352            }
353            return elementsMatch<Index + 1>();
354        }
355        template <size_t Index>
356        static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type
357        elementsMatch() {
358            return true;
359        }
360
361    public:
362        static constexpr bool value = elementsMatch<0>();
363    };
364
365    // Since we assume that pointer arguments are outputs, we can use this template struct to
366    // determine whether or not a given argument is fundamentally a pointer type and thus an output
367    template <typename T>
368    struct IsPointerIfDecayed {
369    private:
370        using Decayed = typename std::decay<T>::type;
371
372    public:
373        static constexpr bool value = std::is_pointer<Decayed>::value;
374    };
375
376    template <typename T>
377    typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
378            Parcel* data, T&& t) const {
379        return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward<T>(t));
380    }
381    template <typename T>
382    typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type writeIfInput(
383            Parcel* /*data*/, T&& /*t*/) const {
384        return NO_ERROR;
385    }
386
387    // This method iterates through all of the arguments, writing them to the data Parcel if they
388    // are an input (i.e., if they are not a pointer type)
389    template <typename T, typename... Remaining>
390    status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const {
391        status_t error = writeIfInput(data, std::forward<T>(t));
392        if (CC_UNLIKELY(error != NO_ERROR)) {
393            // A message will have been logged by writeIfInput
394            return error;
395        }
396        return writeInputs(data, std::forward<Remaining>(remaining)...);
397    }
398    static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; }
399
400    template <typename T>
401    typename std::enable_if<IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
402            const Parcel& reply, T&& t) const {
403        return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward<T>(t));
404    }
405    template <typename T>
406    static typename std::enable_if<!IsPointerIfDecayed<T>::value, status_t>::type readIfOutput(
407            const Parcel& /*reply*/, T&& /*t*/) {
408        return NO_ERROR;
409    }
410
411    // Similar to writeInputs except that it reads output arguments from the reply Parcel
412    template <typename T, typename... Remaining>
413    status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const {
414        status_t error = readIfOutput(reply, std::forward<T>(t));
415        if (CC_UNLIKELY(error != NO_ERROR)) {
416            // A message will have been logged by readIfOutput
417            return error;
418        }
419        return readOutputs(reply, std::forward<Remaining>(remaining)...);
420    }
421    static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; }
422};
423
424template <typename Interface>
425class SafeBnInterface : public BnInterface<Interface> {
426public:
427    explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {}
428
429protected:
430    template <typename Method>
431    status_t callLocal(const Parcel& data, Parcel* reply, Method method) {
432        CHECK_INTERFACE(this, data, reply);
433
434        // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
435        // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
436        // outputs. When we ultimately call into the method, we will pass the addresses of the
437        // output arguments instead of their tuple members directly, but the storage will live in
438        // the tuple.
439        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
440        typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
441
442        // Read the inputs from the data Parcel into the argument tuple
443        status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
444        if (CC_UNLIKELY(error != NO_ERROR)) {
445            // A message will have been logged by read
446            return error;
447        }
448
449        // Call the local method
450        status_t result = MethodCaller<ParamTuple>::call(this, method, &rawArgs);
451
452        // Extract the outputs from the argument tuple and write them into the reply Parcel
453        error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs);
454        if (CC_UNLIKELY(error != NO_ERROR)) {
455            // A message will have been logged by write
456            return error;
457        }
458
459        // Return the result code in the reply Parcel
460        error = reply->writeInt32(result);
461        if (CC_UNLIKELY(error != NO_ERROR)) {
462            ALOG(LOG_ERROR, mLogTag, "Failed to write result");
463#if SI_DUMP_CALLSTACKS
464            CallStack callStack(mLogTag);
465#endif
466            return error;
467        }
468        return NO_ERROR;
469    }
470
471    template <typename Method>
472    status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) {
473        // reply is not actually used by CHECK_INTERFACE
474        CHECK_INTERFACE(this, data, reply);
475
476        // Since we need to both pass inputs into the call as well as retrieve outputs, we create a
477        // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the
478        // outputs. When we ultimately call into the method, we will pass the addresses of the
479        // output arguments instead of their tuple members directly, but the storage will live in
480        // the tuple.
481        using ParamTuple = typename SafeInterface::ParamExtractor<Method>::ParamTuple;
482        typename RawConverter<std::tuple<>, ParamTuple>::type rawArgs{};
483
484        // Read the inputs from the data Parcel into the argument tuple
485        status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
486        if (CC_UNLIKELY(error != NO_ERROR)) {
487            // A message will have been logged by read
488            return error;
489        }
490
491        // Call the local method
492        MethodCaller<ParamTuple>::callVoid(this, method, &rawArgs);
493
494        // After calling, there is nothing more to do since asynchronous calls do not return a value
495        // to the caller
496        return NO_ERROR;
497    }
498
499private:
500    const char* const mLogTag;
501
502    // RemoveFirst strips the first element from a tuple.
503    // For example, given T = std::tuple<A, B, C>, RemoveFirst<T>::type = std::tuple<B, C>
504    template <typename T, typename... Args>
505    struct RemoveFirst;
506    template <typename T, typename... Args>
507    struct RemoveFirst<std::tuple<T, Args...>> {
508        using type = std::tuple<Args...>;
509    };
510
511    // RawConverter strips a tuple down to its fundamental types, discarding both pointers and
512    // references. This allows us to allocate storage for both input (non-pointer) arguments and
513    // output (pointer) arguments in one tuple.
514    // For example, given T = std::tuple<const A&, B*>, RawConverter<T>::type = std::tuple<A, B>
515    template <typename Unconverted, typename... Converted>
516    struct RawConverter;
517    template <typename Unconverted, typename... Converted>
518    struct RawConverter<std::tuple<Converted...>, Unconverted> {
519    private:
520        using ElementType = typename std::tuple_element<0, Unconverted>::type;
521        using Decayed = typename std::decay<ElementType>::type;
522        using WithoutPointer = typename std::remove_pointer<Decayed>::type;
523
524    public:
525        using type = typename RawConverter<std::tuple<Converted..., WithoutPointer>,
526                                           typename RemoveFirst<Unconverted>::type>::type;
527    };
528    template <typename... Converted>
529    struct RawConverter<std::tuple<Converted...>, std::tuple<>> {
530        using type = std::tuple<Converted...>;
531    };
532
533    // This provides a simple way to determine whether the indexed element of Args... is a pointer
534    template <size_t I, typename... Args>
535    struct ElementIsPointer {
536    private:
537        using ElementType = typename std::tuple_element<I, std::tuple<Args...>>::type;
538
539    public:
540        static constexpr bool value = std::is_pointer<ElementType>::value;
541    };
542
543    // This class iterates over the parameter types, and if a given parameter is an input
544    // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel
545    template <typename... Params>
546    class InputReader;
547    template <typename... Params>
548    class InputReader<std::tuple<Params...>> {
549    public:
550        explicit InputReader(const char* logTag) : mLogTag(logTag) {}
551
552        // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit
553        // index (starting with 0 here) instead of using recursion and stripping the first element.
554        // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are
555        // instead just using a tuple as a convenient container for variadic types, whereas here we
556        // can't modify the argument tuple without causing unnecessary copies or moves of the data
557        // contained therein.
558        template <typename RawTuple>
559        status_t readInputs(const Parcel& data, RawTuple* args) {
560            return dispatchArg<0>(data, args);
561        }
562
563    private:
564        const char* const mLogTag;
565
566        template <std::size_t I, typename RawTuple>
567        typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
568                const Parcel& data, RawTuple* args) {
569            return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get<I>(*args));
570        }
571        template <std::size_t I, typename RawTuple>
572        typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type readIfInput(
573                const Parcel& /*data*/, RawTuple* /*args*/) {
574            return NO_ERROR;
575        }
576
577        // Recursively iterate through the arguments
578        template <std::size_t I, typename RawTuple>
579        typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
580                const Parcel& data, RawTuple* args) {
581            status_t error = readIfInput<I>(data, args);
582            if (CC_UNLIKELY(error != NO_ERROR)) {
583                // A message will have been logged in read
584                return error;
585            }
586            return dispatchArg<I + 1>(data, args);
587        }
588        template <std::size_t I, typename RawTuple>
589        typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
590                const Parcel& /*data*/, RawTuple* /*args*/) {
591            return NO_ERROR;
592        }
593    };
594
595    // getForCall uses the types of the parameters to determine whether a given element of the
596    // argument tuple is an input, which should be passed directly into the call, or an output, for
597    // which its address should be passed into the call
598    template <size_t I, typename RawTuple, typename... Params>
599    static typename std::enable_if<
600            ElementIsPointer<I, Params...>::value,
601            typename std::tuple_element<I, std::tuple<Params...>>::type>::type
602    getForCall(RawTuple* args) {
603        return &std::get<I>(*args);
604    }
605    template <size_t I, typename RawTuple, typename... Params>
606    static typename std::enable_if<
607            !ElementIsPointer<I, Params...>::value,
608            typename std::tuple_element<I, std::tuple<Params...>>::type>::type&
609    getForCall(RawTuple* args) {
610        return std::get<I>(*args);
611    }
612
613    // This template class uses std::index_sequence and parameter pack expansion to call the given
614    // method using the elements of the argument tuple (after those arguments are passed through
615    // getForCall to get addresses instead of values for output arguments)
616    template <typename... Params>
617    struct MethodCaller;
618    template <typename... Params>
619    struct MethodCaller<std::tuple<Params...>> {
620    public:
621        // The calls through these to the helper methods are necessary to generate the
622        // std::index_sequences used to unpack the argument tuple into the method call
623        template <typename Class, typename MemberFunction, typename RawTuple>
624        static status_t call(Class* instance, MemberFunction function, RawTuple* args) {
625            return callHelper(instance, function, args, std::index_sequence_for<Params...>{});
626        }
627        template <typename Class, typename MemberFunction, typename RawTuple>
628        static void callVoid(Class* instance, MemberFunction function, RawTuple* args) {
629            callVoidHelper(instance, function, args, std::index_sequence_for<Params...>{});
630        }
631
632    private:
633        template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
634        static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args,
635                                   std::index_sequence<I...> /*unused*/) {
636            return (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
637        }
638        template <typename Class, typename MemberFunction, typename RawTuple, std::size_t... I>
639        static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args,
640                                   std::index_sequence<I...> /*unused*/) {
641            (instance->*function)(getForCall<I, RawTuple, Params...>(args)...);
642        }
643    };
644
645    // This class iterates over the parameter types, and if a given parameter is an output
646    // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel
647    template <typename... Params>
648    struct OutputWriter;
649    template <typename... Params>
650    struct OutputWriter<std::tuple<Params...>> {
651    public:
652        explicit OutputWriter(const char* logTag) : mLogTag(logTag) {}
653
654        // See the note on InputReader::readInputs for why this differs from the arguably simpler
655        // RemoveFirst approach in SafeBpInterface
656        template <typename RawTuple>
657        status_t writeOutputs(Parcel* reply, RawTuple* args) {
658            return dispatchArg<0>(reply, args);
659        }
660
661    private:
662        const char* const mLogTag;
663
664        template <std::size_t I, typename RawTuple>
665        typename std::enable_if<ElementIsPointer<I, Params...>::value, status_t>::type
666        writeIfOutput(Parcel* reply, RawTuple* args) {
667            return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get<I>(*args));
668        }
669        template <std::size_t I, typename RawTuple>
670        typename std::enable_if<!ElementIsPointer<I, Params...>::value, status_t>::type
671        writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) {
672            return NO_ERROR;
673        }
674
675        // Recursively iterate through the arguments
676        template <std::size_t I, typename RawTuple>
677        typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
678                Parcel* reply, RawTuple* args) {
679            status_t error = writeIfOutput<I>(reply, args);
680            if (CC_UNLIKELY(error != NO_ERROR)) {
681                // A message will have been logged in read
682                return error;
683            }
684            return dispatchArg<I + 1>(reply, args);
685        }
686        template <std::size_t I, typename RawTuple>
687        typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg(
688                Parcel* /*reply*/, RawTuple* /*args*/) {
689            return NO_ERROR;
690        }
691    };
692};
693
694} // namespace android
695