• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 IORAP_BINDER_AUTO_PARCELABLE_H_
18 #define IORAP_BINDER_AUTO_PARCELABLE_H_
19 
20 #include "binder/Parcelable.h"
21 #include "binder/Parcel.h"
22 #include "binder/Status.h"
23 
24 #include "common/introspection.h"
25 
26 namespace iorap {
27 namespace binder {
28 
29 //
30 // Implements the android::Parcelable interface (readFromParcel, writeToParcel)
31 // automatically by using compile-time introspection on T.
32 //
33 // Requires that 'T' implements introspection by using the IORAP_INTROSPECT_ADAPT_STRUCT macro.
34 //
35 template <typename T>
36 struct AutoParcelable : public ::android::Parcelable {
37  private:
38   using Status = android::binder::Status;
39   using Parcel = android::Parcel;
40   using Parcelable = android::Parcelable;
41   using status_t = android::status_t;
42 
43  public:
44   // Write every (introspected) field to the parcel by automatically inferring the correct
45   // write method to invoke on the parcel from the member type.
writeToParcelAutoParcelable46   status_t writeToParcel(Parcel* parcel) const override {
47     if (parcel == nullptr) {
48       return ::android::UNEXPECTED_NULL;
49     }
50 
51     status_t result = android::NO_ERROR;
52     ::iorap::introspect::for_each_member_field_value(*Self(), [&](auto&& value) {
53       if (result == android::NO_ERROR) {
54         result = writeAnyToParcel(/*inout*/parcel, value);
55       }
56     });
57 
58     return result;
59   }
60 
61   // Read every (introspected) field to the parcel by automatically inferring the correct
62   // read method to invoke on the parcel from the member type.
63   //
64   // Resilient to partial read failures: A return code other than NO_ERROR means that
65   // the current value is left unmodified.
readFromParcelAutoParcelable66   status_t readFromParcel(const Parcel* parcel) override {
67     if (parcel == nullptr) {
68       return ::android::UNEXPECTED_NULL;
69     }
70 
71     T tmp{*Self()};
72 
73     // Unpack all the parcelable data into a temporary copy.
74     // Parceling could fail halfway through, in which case
75     // the original object is unaffected.
76 
77     status_t result = android::NO_ERROR;
78     ::iorap::introspect::for_each_member_field_set_value(tmp, [&](auto field_type) {
79       // type<?> field_type
80 
81       using ValueT = typename decltype(field_type)::type;
82 
83       if (result == android::NO_ERROR) {
84         auto&& [res, read_value] = readAnyFromParcel<ValueT>(/*inout*/parcel);
85         result = res;
86         return ::iorap::introspect::aliasing_forward<ValueT>(read_value);
87       } else {
88         // TODO: nice-to-have fold over members to early-out on failure.
89         return ValueT{};
90       }
91     });
92 
93     if (result != android::NO_ERROR) {
94       return result;
95     }
96 
97     // Success! Now we can copy all the data in a single step.
98     *Self() = std::move(tmp);
99 
100     // TODO: nice-to-have some kind of invariants-checking after reading the parcel data.
101 
102     return ::android::NO_ERROR;
103   }
104 
105  private:
106 #define AUTO_PARCELABLE_BINDER_MAPPING(FN) \
107 FN(Byte,int8_t)\
108 FN(Int32,int32_t)\
109 FN(Uint32,uint32_t)\
110 FN(Int64,int64_t)\
111 FN(Uint64,uint64_t)\
112 FN(Float,float)\
113 FN(Double,double)\
114 FN(Bool,bool)\
115 FN(CString,const char*)\
116 FN(String16,const String16&)\
117 FN(String16,const std::unique_ptr<String16>&)\
118 FN(StrongBinder,const sp<IBinder>&)\
119 FN(WeakBinder,const wp<IBinder>&)\
120 
121   template <typename F>
writeAnyToParcelAutoParcelable122   static status_t writeAnyToParcel(Parcel* parcel, const F& value) {
123     using namespace android;  // NOLINT
124 
125     // 'F' is the original type of the field here, so it's safe to use it undecayed.
126     // However, to make matching easier we almost always want to match against the decayed type.
127     using D = std::decay_t<F>;  // [const] [volatile] X[&][&] -> X
128 
129     if constexpr (std::is_base_of_v<Parcelable, D>) {
130       return value.writeToParcel(parcel);
131     } else if constexpr (std::is_enum_v<D>) {
132       return writeAnyToParcel(parcel, static_cast<std::underlying_type_t<F>>(value));
133 #define AUTO_PARCELABLE_WRITE_TO_PARCEL(fn_name, type_name) \
134     } else if constexpr (std::is_same_v<D, std::decay_t<type_name>>) { \
135       return parcel->write ## fn_name (value);
136 AUTO_PARCELABLE_BINDER_MAPPING(AUTO_PARCELABLE_WRITE_TO_PARCEL)
137     } else if constexpr (std::is_same_v<D, std::string>) {
138       return parcel->writeUtf8AsUtf16(value);
139     } else {
140       STATIC_FAIL(D, "Unsupported type: Add more manual type conversions above^^^");
141     }
142 
143 #undef AUTO_PARCELABLE_WRITE_TO_PARCEL
144   }
145 
146   template <typename F>
readAnyFromParcelAutoParcelable147   static auto readAnyFromParcel(const Parcel* parcel) {
148     // returns pair(status_t, ~F~)
149     using namespace android;
150 
151     // Since 'F' is almost always an lvalue reference (due to F=decltype(auto&&),
152     // we should lose the references, and also any consts.
153     using D = std::decay_t<F>;
154 
155     D value;
156     status_t result;
157 
158     if constexpr (std::is_base_of_v<Parcelable, D>) {
159       status_t result = value.readFromParcel(/*in*/parcel);
160     } else if constexpr (std::is_enum_v<D>) {
161       auto&& [res, val] = readAnyFromParcel<std::underlying_type_t<D>>(parcel);
162       result = res;
163       value = static_cast<D>(val);
164 #define AUTO_PARCELABLE_READ_FROM_PARCEL(fn_name, type_name) \
165     } else if constexpr (std::is_same_v<D, std::decay_t<type_name>>) { \
166       result = parcel->read ## fn_name (/*out*/&value);
167 AUTO_PARCELABLE_BINDER_MAPPING(AUTO_PARCELABLE_READ_FROM_PARCEL)
168     } else if constexpr (std::is_same_v<D, std::string>) {
169       result = parcel->readUtf8FromUtf16(/*out*/&value);
170     } else {
171       STATIC_FAIL(D, "Unsupported type: Add more manual type conversions above^^^");
172     }
173 #undef AUTO_PARCELABLE_READ_FROM_PARCEL
174 
175     return std::make_pair(result, std::move(value));
176   }
177 
SelfAutoParcelable178   T* Self() {
179     return static_cast<T*>(this);
180   }
SelfAutoParcelable181   const T* Self() const {
182     return static_cast<const T*>(this);
183   }
184 };
185 
186 }  // namespace binder
187 }  // namespace iorap
188 
189 #endif  // IORAP_BINDER_AUTO_PARCELABLE_H_
190