You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
6.1 KiB
189 lines
6.1 KiB
/*
|
|
* Copyright (C) 2018 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 IORAP_BINDER_AUTO_PARCELABLE_H_
|
|
#define IORAP_BINDER_AUTO_PARCELABLE_H_
|
|
|
|
#include "binder/Parcelable.h"
|
|
#include "binder/Parcel.h"
|
|
#include "binder/Status.h"
|
|
|
|
#include "common/introspection.h"
|
|
|
|
namespace iorap {
|
|
namespace binder {
|
|
|
|
//
|
|
// Implements the android::Parcelable interface (readFromParcel, writeToParcel)
|
|
// automatically by using compile-time introspection on T.
|
|
//
|
|
// Requires that 'T' implements introspection by using the IORAP_INTROSPECT_ADAPT_STRUCT macro.
|
|
//
|
|
template <typename T>
|
|
struct AutoParcelable : public ::android::Parcelable {
|
|
private:
|
|
using Status = android::binder::Status;
|
|
using Parcel = android::Parcel;
|
|
using Parcelable = android::Parcelable;
|
|
using status_t = android::status_t;
|
|
|
|
public:
|
|
// Write every (introspected) field to the parcel by automatically inferring the correct
|
|
// write method to invoke on the parcel from the member type.
|
|
status_t writeToParcel(Parcel* parcel) const override {
|
|
if (parcel == nullptr) {
|
|
return ::android::UNEXPECTED_NULL;
|
|
}
|
|
|
|
status_t result = android::NO_ERROR;
|
|
::iorap::introspect::for_each_member_field_value(*Self(), [&](auto&& value) {
|
|
if (result == android::NO_ERROR) {
|
|
result = writeAnyToParcel(/*inout*/parcel, value);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
// Read every (introspected) field to the parcel by automatically inferring the correct
|
|
// read method to invoke on the parcel from the member type.
|
|
//
|
|
// Resilient to partial read failures: A return code other than NO_ERROR means that
|
|
// the current value is left unmodified.
|
|
status_t readFromParcel(const Parcel* parcel) override {
|
|
if (parcel == nullptr) {
|
|
return ::android::UNEXPECTED_NULL;
|
|
}
|
|
|
|
T tmp{*Self()};
|
|
|
|
// Unpack all the parcelable data into a temporary copy.
|
|
// Parceling could fail halfway through, in which case
|
|
// the original object is unaffected.
|
|
|
|
status_t result = android::NO_ERROR;
|
|
::iorap::introspect::for_each_member_field_set_value(tmp, [&](auto field_type) {
|
|
// type<?> field_type
|
|
|
|
using ValueT = typename decltype(field_type)::type;
|
|
|
|
if (result == android::NO_ERROR) {
|
|
auto&& [res, read_value] = readAnyFromParcel<ValueT>(/*inout*/parcel);
|
|
result = res;
|
|
return ::iorap::introspect::aliasing_forward<ValueT>(read_value);
|
|
} else {
|
|
// TODO: nice-to-have fold over members to early-out on failure.
|
|
return ValueT{};
|
|
}
|
|
});
|
|
|
|
if (result != android::NO_ERROR) {
|
|
return result;
|
|
}
|
|
|
|
// Success! Now we can copy all the data in a single step.
|
|
*Self() = std::move(tmp);
|
|
|
|
// TODO: nice-to-have some kind of invariants-checking after reading the parcel data.
|
|
|
|
return ::android::NO_ERROR;
|
|
}
|
|
|
|
private:
|
|
#define AUTO_PARCELABLE_BINDER_MAPPING(FN) \
|
|
FN(Byte,int8_t)\
|
|
FN(Int32,int32_t)\
|
|
FN(Uint32,uint32_t)\
|
|
FN(Int64,int64_t)\
|
|
FN(Uint64,uint64_t)\
|
|
FN(Float,float)\
|
|
FN(Double,double)\
|
|
FN(Bool,bool)\
|
|
FN(CString,const char*)\
|
|
FN(String16,const String16&)\
|
|
FN(String16,const std::unique_ptr<String16>&)\
|
|
FN(StrongBinder,const sp<IBinder>&)\
|
|
|
|
template <typename F>
|
|
static status_t writeAnyToParcel(Parcel* parcel, const F& value) {
|
|
using namespace android; // NOLINT
|
|
|
|
// 'F' is the original type of the field here, so it's safe to use it undecayed.
|
|
// However, to make matching easier we almost always want to match against the decayed type.
|
|
using D = std::decay_t<F>; // [const] [volatile] X[&][&] -> X
|
|
|
|
if constexpr (std::is_base_of_v<Parcelable, D>) {
|
|
return value.writeToParcel(parcel);
|
|
} else if constexpr (std::is_enum_v<D>) {
|
|
return writeAnyToParcel(parcel, static_cast<std::underlying_type_t<F>>(value));
|
|
#define AUTO_PARCELABLE_WRITE_TO_PARCEL(fn_name, type_name) \
|
|
} else if constexpr (std::is_same_v<D, std::decay_t<type_name>>) { \
|
|
return parcel->write ## fn_name (value);
|
|
AUTO_PARCELABLE_BINDER_MAPPING(AUTO_PARCELABLE_WRITE_TO_PARCEL)
|
|
} else if constexpr (std::is_same_v<D, std::string>) {
|
|
return parcel->writeUtf8AsUtf16(value);
|
|
} else {
|
|
STATIC_FAIL(D, "Unsupported type: Add more manual type conversions above^^^");
|
|
}
|
|
|
|
#undef AUTO_PARCELABLE_WRITE_TO_PARCEL
|
|
}
|
|
|
|
template <typename F>
|
|
static auto readAnyFromParcel(const Parcel* parcel) {
|
|
// returns pair(status_t, ~F~)
|
|
using namespace android;
|
|
|
|
// Since 'F' is almost always an lvalue reference (due to F=decltype(auto&&),
|
|
// we should lose the references, and also any consts.
|
|
using D = std::decay_t<F>;
|
|
|
|
D value;
|
|
status_t result;
|
|
|
|
if constexpr (std::is_base_of_v<Parcelable, D>) {
|
|
status_t result = value.readFromParcel(/*in*/parcel);
|
|
} else if constexpr (std::is_enum_v<D>) {
|
|
auto&& [res, val] = readAnyFromParcel<std::underlying_type_t<D>>(parcel);
|
|
result = res;
|
|
value = static_cast<D>(val);
|
|
#define AUTO_PARCELABLE_READ_FROM_PARCEL(fn_name, type_name) \
|
|
} else if constexpr (std::is_same_v<D, std::decay_t<type_name>>) { \
|
|
result = parcel->read ## fn_name (/*out*/&value);
|
|
AUTO_PARCELABLE_BINDER_MAPPING(AUTO_PARCELABLE_READ_FROM_PARCEL)
|
|
} else if constexpr (std::is_same_v<D, std::string>) {
|
|
result = parcel->readUtf8FromUtf16(/*out*/&value);
|
|
} else {
|
|
STATIC_FAIL(D, "Unsupported type: Add more manual type conversions above^^^");
|
|
}
|
|
#undef AUTO_PARCELABLE_READ_FROM_PARCEL
|
|
|
|
return std::make_pair(result, std::move(value));
|
|
}
|
|
|
|
T* Self() {
|
|
return static_cast<T*>(this);
|
|
}
|
|
const T* Self() const {
|
|
return static_cast<const T*>(this);
|
|
}
|
|
};
|
|
|
|
} // namespace binder
|
|
} // namespace iorap
|
|
|
|
#endif // IORAP_BINDER_AUTO_PARCELABLE_H_
|