1 /*
2  * Copyright (C) 2021 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 CPP_CAR_BINDER_LIB_LARGEPARCELABLE_INCLUDE_LARGEPARCELABLEBASE_H_
18 #define CPP_CAR_BINDER_LIB_LARGEPARCELABLE_INCLUDE_LARGEPARCELABLEBASE_H_
19 
20 #include "SharedMemory.h"
21 
22 #include <android-base/result.h>
23 #include <android-base/unique_fd.h>
24 #include <android/binder_auto_utils.h>
25 #include <android/binder_parcel.h>
26 #include <android/binder_parcel_utils.h>
27 #include <android/binder_status.h>
28 #include <utils/Log.h>
29 
30 #include <memory>
31 #include <optional>
32 
33 namespace android {
34 namespace automotive {
35 namespace car_binder_lib {
36 
37 // Base class to allow passing a 'Parcelable' over binder directly or through shared memory if
38 // payload size exceeds MAX_DIRECT_PAYLOAD_SIZE.
39 //
40 // <p>Child class should inherit this to use this or use 'LargeParcelable' class.
41 //
42 // <p>Parcelized data will have following elements
43 // <ul>
44 // <li>@Nullable Parcelable
45 // <li>@Nullable SharedMemory which includes serialized Parcelable if non-null. This will be set
46 // only when the previous Parcelable is null or this also can be null for no data case.
47 // </ul>
48 //
49 // @hide
50 class LargeParcelableBase {
51 public:
52     // Payload size bigger than this value will be passed over shared memory.
53     static constexpr int32_t MAX_DIRECT_PAYLOAD_SIZE = 4096;
54 
55     LargeParcelableBase() = default;
56 
57     virtual ~LargeParcelableBase() = default;
58 
59     // Initialize this parcelable with input parcel.
60     binder_status_t readFromParcel(const AParcel* parcel);
61 
62     // Write the owned parcelable object to the given |parcel|.
63     binder_status_t writeToParcel(AParcel* parcel) const;
64 
65     // This is a class that either borrows T or owns T.
66     template <class T>
67     class BorrowedOwnedObject {
68     public:
BorrowedOwnedObject(const T * o)69         explicit BorrowedOwnedObject(const T* o) {
70             mBorrowed = o;
71             mOwned = nullptr;
72         }
73 
BorrowedOwnedObject(std::unique_ptr<T> o)74         explicit BorrowedOwnedObject(std::unique_ptr<T> o) {
75             mBorrowed = nullptr;
76             mOwned = std::move(o);
77         }
78 
getObject()79         const T* getObject() {
80             if (mBorrowed == nullptr) {
81                 return mOwned.get();
82             }
83             return mBorrowed;
84         }
85 
86     private:
87         const T* mBorrowed = nullptr;
88         std::unique_ptr<T> mOwned = nullptr;
89     };
90     // Write the input parcelable into a shared memory file that could be passed across binder if
91     // the parcel generated by 'in' is larger than MAX_DIRECT_PAYLOAD_SIZE.
92     // Returns error if input could not be serialized.
93     // Returns a {@Code ScopedFileDescriptor} object if the input has been serialized to
94     // the returned shared memory file.
95     // Returns nullptr if the input is small enough and could be directly sent through binder.
96     template <class T>
97     static ::android::base::Result<std::unique_ptr<::ndk::ScopedFileDescriptor>>
parcelableToStableLargeParcelable(const T & in)98     parcelableToStableLargeParcelable(const T& in) {
99         std::unique_ptr<::ndk::ScopedFileDescriptor> sharedMemoryFd =
100                 std::make_unique<::ndk::ScopedFileDescriptor>();
101         ::ndk::ScopedAParcel parcel(AParcel_create());
102 
103         if (binder_status_t status = in.writeToParcel(parcel.get()); status != STATUS_OK) {
104             return ::android::base::Error(status) << "failed to write parcelable to parcel";
105         }
106         int32_t payloadSize = AParcel_getDataPosition(parcel.get());
107         bool noSharedMemory = (payloadSize <= MAX_DIRECT_PAYLOAD_SIZE);
108         if (noSharedMemory) {
109             return nullptr;
110         }
111         if (binder_status_t status = parcelToMemoryFile(*parcel.get(), sharedMemoryFd.get());
112             status != STATUS_OK) {
113             return ::android::base::Error(status) << "failed to write parcel as shared memory file";
114         }
115         return sharedMemoryFd;
116     }
117 
118     // Turns a largeParcelable received through binder back to the original parcelable with payload.
119     // This is the opposite operation for 'parcelableToStableLargeParcelable'.
120     // If the input contains the 'sharedMemoryFd' field, it would be deserialized to the content
121     // field for output. Otherwise, the borrowed original input would be returned.
122     // Returns error if shared memory file could not be deserialized.
123     // The output must not outlive the input's lifetime. Caller should use getObject() to get
124     // a const pointer to output T.
125     template <class T>
stableLargeParcelableToParcelable(const T & in)126     static ::android::base::Result<BorrowedOwnedObject<T>> stableLargeParcelableToParcelable(
127             const T& in) {
128         const ::ndk::ScopedFileDescriptor& sharedMemoryFd = in.sharedMemoryFd;
129         if (sharedMemoryFd.get() == INVALID_MEMORY_FD) {
130             return BorrowedOwnedObject(&in);
131         }
132 
133         ::ndk::ScopedAParcel parcel(AParcel_create());
134         if (binder_status_t status = getParcelFromMemoryFile(sharedMemoryFd, parcel.get());
135             status != STATUS_OK) {
136             return ::android::base::Error(status) << "failed to get parcel from memory file";
137         }
138 
139         std::unique_ptr<T> out = std::make_unique<T>();
140         if (binder_status_t status = out->readFromParcel(parcel.get()); status != STATUS_OK) {
141             return ::android::base::Error(status)
142                     << "failed to read from parcel from shared memory";
143         }
144         return BorrowedOwnedObject(std::move(out));
145     }
146 
147 protected:
148     static constexpr bool DBG_PAYLOAD = false;
149     static constexpr size_t DBG_DUMP_LENGTH = 64;
150 
151     // Whether this object contains a valid parcelable from a previous successful
152     // {@Code readFromParcel} call. Subclass must use this function to check before returning
153     // the deserialized parcelable object.
154     bool hasDeserializedParcelable() const;
155 
156     // Serialize (=write Parcelable into given Parcel) a {@code Parcelable} child class wants to
157     // pass over binder call.
158     virtual binder_status_t serialize(AParcel* dest) const = 0;
159 
160     // Serialize null payload to the given {@code Parcel}. For {@code Parcelable}, this can be
161     // simply {@code dest.writeParcelable(null)} but non-Parcelable should have other way to
162     // mark that there is no payload.
163     virtual binder_status_t serializeNullPayload(AParcel* dest) const = 0;
164 
165     // Read a {@code Parcelable} from the given {@code Parcel}.
166     virtual binder_status_t deserialize(const AParcel& src) = 0;
167 
168     static std::unique_ptr<SharedMemory> serializeParcelToSharedMemory(const AParcel& p,
169                                                                        int32_t start, int32_t size,
170                                                                        binder_status_t* outStatus);
171 
172     static binder_status_t copyFromSharedMemory(const SharedMemory& sharedMemory, AParcel* parcel);
173 
174     binder_status_t deserializeSharedMemoryAndClose(::android::base::unique_fd memoryFd);
175 
176     static binder_status_t getParcelFromMemoryFile(const ::ndk::ScopedFileDescriptor& fd,
177                                                    AParcel* parcel);
178 
179     static binder_status_t parcelToMemoryFile(const AParcel& parcel,
180                                               ::ndk::ScopedFileDescriptor* sharedMemoryFd);
181 
182 private:
183     static constexpr int32_t NULL_PAYLOAD = 0;
184     static constexpr int32_t NONNULL_PAYLOAD = 1;
185     static constexpr int32_t FD_HEADER = 0;
186     static constexpr int32_t INVALID_MEMORY_FD = -1;
187 
188     mutable std::optional<bool> mNeedSharedMemory = std::nullopt;
189     mutable std::unique_ptr<SharedMemory> mSharedMemory;
190 
191     // Whether the contained parcelable is valid.
192     bool mHasDeserializedParcelable = false;
193 
194     // Write shared memory in compatible way with ParcelFileDescriptor
195     static binder_status_t writeSharedMemoryCompatibleToParcel(const SharedMemory* sharedMemory,
196                                                                AParcel* dest);
197     static int32_t updatePayloadSize(AParcel* dest, int32_t startPosition);
198 
199     // Turns a ::ndk::ScopedFileDescriptor into a borrowed file descriptor.
200     static ::android::base::borrowed_fd scopedFdToBorrowedFd(const ::ndk::ScopedFileDescriptor& fd);
201 
202     // Turns a ::ndk::ScopedFileDescriptor into a borrowed file descriptor. The
203     // ::ndk::ScopedFileDescriptor would lose the ownership to the underlying file descriptor.
204     static ::android::base::unique_fd scopeFdToUniqueFd(::ndk::ScopedFileDescriptor&& fd);
205 
206     // Create a shared memory file containing the marshalled parcelable so that it could be used
207     // in writeToParcel.
208     binder_status_t prepareSharedMemory(AParcel* fd) const;
209 
210     // If sharedMemory is not null, serialize the null payload and the shared memory to 'dest'.
211     // Otherwise, serialize the actual payload and a null memoryFd to 'dest'.
212     binder_status_t serializeMemoryFdOrPayload(AParcel* dest,
213                                                const SharedMemory* sharedMemory) const;
214 };
215 
216 }  // namespace car_binder_lib
217 }  // namespace automotive
218 }  // namespace android
219 
220 #endif  // CPP_CAR_BINDER_LIB_LARGEPARCELABLE_INCLUDE_LARGEPARCELABLEBASE_H_
221