1 /*
2  * Copyright (C) 2020 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 /**
18  * @addtogroup NdkBinder
19  * @{
20  */
21 
22 /**
23  * @file binder_parcelable_utils.h
24  * @brief Helper for parcelable.
25  */
26 
27 #pragma once
28 #include <android/binder_parcel_utils.h>
29 #include <optional>
30 
31 namespace ndk {
32 // Also see Parcelable.h in libbinder.
33 typedef int32_t parcelable_stability_t;
34 enum {
35     STABILITY_LOCAL,
36     STABILITY_VINTF,  // corresponds to @VintfStability
37 };
38 #define RETURN_ON_FAILURE(expr)                   \
39     do {                                          \
40         binder_status_t _status = (expr);         \
41         if (_status != STATUS_OK) return _status; \
42     } while (false)
43 
44 // AParcelableHolder has been introduced in 31.
45 #if __ANDROID_API__ >= 31
46 class AParcelableHolder {
47    public:
48     AParcelableHolder() = delete;
AParcelableHolder(parcelable_stability_t stability)49     explicit AParcelableHolder(parcelable_stability_t stability)
50         : mParcel(AParcel_create()), mStability(stability) {}
51 
AParcelableHolder(const AParcelableHolder & other)52     AParcelableHolder(const AParcelableHolder& other)
53         : mParcel(AParcel_create()), mStability(other.mStability) {
54         AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
55                            AParcel_getDataSize(other.mParcel.get()));
56     }
57 
58     AParcelableHolder(AParcelableHolder&& other) = default;
59     virtual ~AParcelableHolder() = default;
60 
writeToParcel(AParcel * parcel)61     binder_status_t writeToParcel(AParcel* parcel) const {
62         RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
63         int32_t size = AParcel_getDataSize(this->mParcel.get());
64         RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
65         size = AParcel_getDataSize(this->mParcel.get());
66         RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
67         return STATUS_OK;
68     }
69 
readFromParcel(const AParcel * parcel)70     binder_status_t readFromParcel(const AParcel* parcel) {
71         AParcel_reset(mParcel.get());
72 
73         parcelable_stability_t wireStability;
74         RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability));
75         if (this->mStability != wireStability) {
76             return STATUS_BAD_VALUE;
77         }
78 
79         int32_t dataSize;
80         binder_status_t status = AParcel_readInt32(parcel, &dataSize);
81 
82         if (status != STATUS_OK || dataSize < 0) {
83             return status != STATUS_OK ? status : STATUS_BAD_VALUE;
84         }
85 
86         int32_t dataStartPos = AParcel_getDataPosition(parcel);
87 
88         if (dataStartPos > INT32_MAX - dataSize) {
89             return STATUS_BAD_VALUE;
90         }
91 
92         status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
93         if (status != STATUS_OK) {
94             return status;
95         }
96         return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
97     }
98 
99     template <typename T>
setParcelable(const T & p)100     binder_status_t setParcelable(const T& p) {
101         if (this->mStability > T::_aidl_stability) {
102             return STATUS_BAD_VALUE;
103         }
104         AParcel_reset(mParcel.get());
105         AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
106         p.writeToParcel(mParcel.get());
107         return STATUS_OK;
108     }
109 
110     template <typename T>
getParcelable(std::optional<T> * ret)111     binder_status_t getParcelable(std::optional<T>* ret) const {
112         const std::string parcelableDesc(T::descriptor);
113         AParcel_setDataPosition(mParcel.get(), 0);
114         if (AParcel_getDataSize(mParcel.get()) == 0) {
115             *ret = std::nullopt;
116             return STATUS_OK;
117         }
118         std::string parcelableDescInParcel;
119         binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
120         if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
121             *ret = std::nullopt;
122             return status;
123         }
124         *ret = std::make_optional<T>();
125         status = (*ret)->readFromParcel(this->mParcel.get());
126         if (status != STATUS_OK) {
127             *ret = std::nullopt;
128             return status;
129         }
130         return STATUS_OK;
131     }
132 
reset()133     void reset() { AParcel_reset(mParcel.get()); }
134 
135     inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
136     inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
137     inline bool operator<=(const AParcelableHolder& rhs) const { return this <= &rhs; }
138     inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
139     inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
140     inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
141     inline AParcelableHolder& operator=(const AParcelableHolder& rhs) {
142         this->reset();
143         if (this->mStability != rhs.mStability) {
144             syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
145                    this->mStability, rhs.mStability);
146             abort();
147         }
148         AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
149                            AParcel_getDataSize(rhs.mParcel.get()));
150         return *this;
151     }
152 
153    private:
154     mutable ndk::ScopedAParcel mParcel;
155     parcelable_stability_t mStability;
156 };
157 #endif  // __ANDROID_API__ >= 31
158 
159 #undef RETURN_ON_FAILURE
160 }  // namespace ndk
161 
162 /** @} */
163