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