blob: 972eca79ff4e78a4a8c57f5c80061302494f4ac9 [file] [log] [blame]
/*
* Copyright (C) 2020 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.
*/
/**
* @addtogroup NdkBinder
* @{
*/
/**
* @file binder_parcelable_utils.h
* @brief Helper for parcelable.
*/
#pragma once
#include <android/binder_parcel_utils.h>
#include <optional>
namespace ndk {
// Also see Parcelable.h in libbinder.
typedef int32_t parcelable_stability_t;
enum {
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
#define RETURN_ON_FAILURE(expr) \
do { \
binder_status_t _status = (expr); \
if (_status != STATUS_OK) return _status; \
} while (false)
class AParcelableHolder {
public:
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
#if __ANDROID_API__ >= 31
AParcelableHolder(const AParcelableHolder& other)
: mParcel(AParcel_create()), mStability(other.mStability) {
// AParcelableHolder has been introduced in 31.
if (__builtin_available(android 31, *)) {
AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
AParcel_getDataSize(other.mParcel.get()));
}
}
#endif
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
if (__builtin_available(android 31, *)) {
int32_t size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
} else {
return STATUS_INVALID_OPERATION;
}
if (__builtin_available(android 31, *)) {
int32_t size = AParcel_getDataSize(this->mParcel.get());
RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
} else {
return STATUS_INVALID_OPERATION;
}
return STATUS_OK;
}
binder_status_t readFromParcel(const AParcel* parcel) {
if (__builtin_available(android 31, *)) {
AParcel_reset(mParcel.get());
} else {
return STATUS_INVALID_OPERATION;
}
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
int32_t dataSize;
binder_status_t status = AParcel_readInt32(parcel, &dataSize);
if (status != STATUS_OK || dataSize < 0) {
return status != STATUS_OK ? status : STATUS_BAD_VALUE;
}
int32_t dataStartPos = AParcel_getDataPosition(parcel);
if (dataStartPos > INT32_MAX - dataSize) {
return STATUS_BAD_VALUE;
}
if (__builtin_available(android 31, *)) {
status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
} else {
status = STATUS_INVALID_OPERATION;
}
if (status != STATUS_OK) {
return status;
}
return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
}
template <typename T>
binder_status_t setParcelable(const T& p) {
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
if (__builtin_available(android 31, *)) {
AParcel_reset(mParcel.get());
} else {
return STATUS_INVALID_OPERATION;
}
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
p.writeToParcel(mParcel.get());
return STATUS_OK;
}
template <typename T>
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
if (__builtin_available(android 31, *)) {
if (AParcel_getDataSize(mParcel.get()) == 0) {
*ret = std::nullopt;
return STATUS_OK;
}
} else {
return STATUS_INVALID_OPERATION;
}
std::string parcelableDescInParcel;
binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
*ret = std::nullopt;
return status;
}
*ret = std::make_optional<T>();
status = (*ret)->readFromParcel(this->mParcel.get());
if (status != STATUS_OK) {
*ret = std::nullopt;
return status;
}
return STATUS_OK;
}
void reset() {
if (__builtin_available(android 31, *)) {
AParcel_reset(mParcel.get());
}
}
inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
inline bool operator<=(const AParcelableHolder& rhs) const { return this <= &rhs; }
inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
private:
mutable ndk::ScopedAParcel mParcel;
parcelable_stability_t mStability;
};
#undef RETURN_ON_FAILURE
} // namespace ndk
/** @} */