android13/packages/services/Car/cpp/car_binder_lib/largeParcelable/LargeParcelableBase.cpp

371 lines
14 KiB
C++

/*
* Copyright (C) 2021 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.
*/
#define LOG_TAG "LargeParcelable"
#include "LargeParcelableBase.h"
#include "MappedFile.h"
#include "SharedMemory.h"
#include <android-base/unique_fd.h>
#include <android/binder_auto_utils.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_utils.h>
#include <android/binder_status.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <stdint.h>
#include <cstring>
#include <iostream>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
namespace android {
namespace automotive {
namespace car_binder_lib {
using ::android::base::borrowed_fd;
using ::android::base::unique_fd;
using ::ndk::ScopedAParcel;
using ::ndk::ScopedFileDescriptor;
borrowed_fd LargeParcelableBase::scopedFdToBorrowedFd(const ScopedFileDescriptor& fd) {
borrowed_fd memoryFd(fd.get());
return memoryFd;
}
unique_fd LargeParcelableBase::scopeFdToUniqueFd(ScopedFileDescriptor&& fd) {
// ScopedFileDescriptor does not have release function, so we have to directly modify its
// underlying fd pointer to remove ownership.
unique_fd memoryFd(fd.get());
*(fd.getR()) = INVALID_MEMORY_FD;
return memoryFd;
}
binder_status_t LargeParcelableBase::readFromParcel(const AParcel* in) {
mHasDeserializedParcelable = false;
// Make this compatible with stable AIDL
// payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor
int32_t startPosition = AParcel_getDataPosition(in);
int32_t totalPayloadSize;
if (binder_status_t status = AParcel_readInt32(in, &totalPayloadSize); status != STATUS_OK) {
ALOGE("failed to read Int32: %d", status);
return status;
}
if (binder_status_t status = deserialize(*in); status != STATUS_OK) {
ALOGE("failed to deserialize: %d", status);
return status;
}
int32_t sharedMemoryPosition = AParcel_getDataPosition(in);
ScopedFileDescriptor descriptor;
if (binder_status_t status = AParcel_readNullableParcelFileDescriptor(in, &descriptor);
status != STATUS_OK) {
ALOGE("invalid data, failed to read file descirptor: %d", status);
return status;
}
bool hasSharedMemory = (descriptor.get() != INVALID_MEMORY_FD);
if (hasSharedMemory) {
// Release descriptor to memoryFd.
unique_fd memoryFd = scopeFdToUniqueFd(std::move(descriptor));
if (binder_status_t status = deserializeSharedMemoryAndClose(std::move(memoryFd));
status != STATUS_OK) {
return status;
}
}
if (DBG_PAYLOAD) {
ALOGD("Read, start:%d totalPayloadSize:%d sharedMemoryPosition:%d hasSharedMemory:%d",
startPosition, totalPayloadSize, sharedMemoryPosition, hasSharedMemory);
}
mHasDeserializedParcelable = true;
return STATUS_OK;
}
binder_status_t LargeParcelableBase::prepareSharedMemory(AParcel* parcel) const {
int32_t startPosition = AParcel_getDataPosition(parcel);
if (binder_status_t status = serializeMemoryFdOrPayload(parcel, nullptr); status != STATUS_OK) {
ALOGE("failed to serialize: %d", status);
return status;
}
int32_t payloadSize = AParcel_getDataPosition(parcel) - startPosition;
bool noSharedMemory = (payloadSize <= MAX_DIRECT_PAYLOAD_SIZE);
if (noSharedMemory) {
// Do nothing.
mNeedSharedMemory = false;
return STATUS_OK;
}
binder_status_t status;
std::unique_ptr<SharedMemory> sharedMemory =
serializeParcelToSharedMemory(*parcel, startPosition, payloadSize, &status);
if (status != STATUS_OK) {
ALOGE("failed to serialize parcel to shared memory: %d", status);
return status;
}
mNeedSharedMemory = true;
mSharedMemory = std::move(sharedMemory);
return STATUS_OK;
}
binder_status_t LargeParcelableBase::writeToParcel(AParcel* dest) const {
// Make this compatible with stable AIDL
// payloadSize + Nullable Parcelable + Nullable ParcelFileDescriptor
int startPosition = AParcel_getDataPosition(dest);
if (!mNeedSharedMemory.has_value()) {
if (binder_status_t status = prepareSharedMemory(dest); status != STATUS_OK) {
ALOGE("failed to serialize payload to parcel: %d", status);
return status;
}
}
bool needSharedMemory = mNeedSharedMemory.value();
if (needSharedMemory) {
const SharedMemory* sharedMemory = mSharedMemory.get();
AParcel_setDataPosition(dest, startPosition);
if (binder_status_t status = serializeMemoryFdOrPayload(dest, sharedMemory);
status != STATUS_OK) {
ALOGE("failed to serialize shared memory fd to parcel: %d", status);
return status;
}
}
int32_t totalPayloadSize = AParcel_getDataPosition(dest) - startPosition;
if (DBG_PAYLOAD) {
ALOGD("Write, start:%d totalPayloadSize:%d hasSharedMemory:%d", startPosition,
totalPayloadSize, needSharedMemory);
}
return OK;
}
binder_status_t LargeParcelableBase::deserializeSharedMemoryAndClose(unique_fd memoryFd) {
ScopedAParcel parcel(AParcel_create());
// This would close memoryFd after destruction.
SharedMemory sharedMemory(std::move(memoryFd));
if (!sharedMemory.isValid()) {
ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr());
return STATUS_FDS_NOT_ALLOWED;
}
if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel.get());
status != STATUS_OK) {
return status;
}
int32_t payloadSize;
if (binder_status_t status = AParcel_readInt32(parcel.get(), &payloadSize);
status != STATUS_OK) {
ALOGE("failed to read Int32: %d", status);
if (DBG_PAYLOAD) {
ALOGD("parse shared memory file, payload size: %d", payloadSize);
}
return status;
}
if (binder_status_t status = deserialize(*(parcel.get())); status != STATUS_OK) {
return status;
}
// There is an additional 0 for null file descriptor in the parcel we would ignore.
return STATUS_OK;
}
binder_status_t LargeParcelableBase::copyFromSharedMemory(const SharedMemory& sharedMemory,
AParcel* parcel) {
std::unique_ptr<MappedFile> mappedFile = sharedMemory.mapReadOnly();
size_t mappedFileSize = mappedFile->getSize();
if (!mappedFile->isValid()) {
ALOGE("failed to map file for size: %zu, error: %d", sharedMemory.getSize(),
mappedFile->getErr());
return STATUS_FDS_NOT_ALLOWED;
}
if (binder_status_t status =
AParcel_unmarshal(parcel, static_cast<const uint8_t*>(mappedFile->getAddr()),
mappedFileSize);
status != STATUS_OK) {
return status;
}
AParcel_setDataPosition(parcel, 0);
if (DBG_PAYLOAD) {
size_t dumpSize = std::min(DBG_DUMP_LENGTH, mappedFileSize);
bool truncated = (dumpSize < mappedFileSize);
std::stringbuf buffer;
std::ostream bd(&buffer);
if (truncated) {
bd << "unmarshalled(truncated):";
} else {
bd << "unmarshalled:";
}
bd << std::hex;
size_t parcelStartPosition = AParcel_getDataPosition(parcel);
std::unique_ptr<uint8_t[]> fromParcel(new uint8_t[mappedFileSize]);
if (binder_status_t status = AParcel_marshal(parcel, fromParcel.get(), 0, dumpSize);
status != STATUS_OK) {
ALOGE("failed to marshal parcel: %d", status);
return status;
}
for (size_t i = 0; i < dumpSize; i++) {
bd << static_cast<int>(fromParcel[i]);
if (i != dumpSize - 1) {
bd << ",";
}
}
bd << "=startPosition:" << parcelStartPosition;
bd.flush();
ALOGD("%s", buffer.str().c_str());
AParcel_setDataPosition(parcel, parcelStartPosition);
}
return STATUS_OK;
}
binder_status_t LargeParcelableBase::writeSharedMemoryCompatibleToParcel(
const SharedMemory* sharedMemory, AParcel* dest) {
ScopedFileDescriptor descriptor;
if (sharedMemory == nullptr) {
return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor);
}
// non-null case
unique_fd fd = sharedMemory->getDupFd();
descriptor.set(fd.release());
return ::ndk::AParcel_writeNullableParcelFileDescriptor(dest, descriptor);
}
std::unique_ptr<SharedMemory> LargeParcelableBase::serializeParcelToSharedMemory(
const AParcel& p, int32_t start, int32_t size, binder_status_t* outStatus) {
std::unique_ptr<SharedMemory> memory(new SharedMemory(size));
if (!memory->isValid()) {
ALOGE("failed to create memfile for size: %d, status: %d", size, memory->getErr());
*outStatus = STATUS_UNKNOWN_ERROR;
return std::unique_ptr<SharedMemory>(nullptr);
}
// This would be unmapped after function returns.
std::unique_ptr<MappedFile> buffer = memory->mapReadWrite();
if (!buffer->isValid()) {
ALOGE("failed to map shared memory as read write for size: %d, status: %d", size,
buffer->getErr());
*outStatus = STATUS_UNKNOWN_ERROR;
return std::unique_ptr<SharedMemory>(nullptr);
}
if (binder_status_t status =
AParcel_marshal(&p, static_cast<uint8_t*>(buffer->getWriteAddr()), start, size);
status != STATUS_OK) {
ALOGE("failed to marshal parcel: %d", status);
*outStatus = status;
return std::unique_ptr<SharedMemory>(nullptr);
}
buffer->sync();
if (status_t astatus = memory->lock() != OK) {
ALOGE("Failed to set read-only protection on shared memory: %d", astatus);
*outStatus = STATUS_UNKNOWN_ERROR;
return std::unique_ptr<SharedMemory>(nullptr);
}
if (DBG_PAYLOAD) {
size_t dumpSize = std::min(DBG_DUMP_LENGTH, static_cast<size_t>(size));
std::stringbuf log;
std::ostream bd(&log);
const uint8_t* addr = static_cast<uint8_t*>(buffer->getWriteAddr());
bd << "unmarshalled:" << std::hex;
for (size_t i = 0; i < dumpSize; i++) {
bd << static_cast<int>(addr[i]);
if (i != dumpSize - 1) {
bd << ",";
}
}
bd.flush();
ALOGD("%s", log.str().c_str());
}
*outStatus = STATUS_OK;
return memory;
}
int32_t LargeParcelableBase::updatePayloadSize(AParcel* dest, int32_t startPosition) {
int32_t lastPosition = AParcel_getDataPosition(dest);
int32_t totalPayloadSize = lastPosition - startPosition;
AParcel_setDataPosition(dest, startPosition);
AParcel_writeInt32(dest, totalPayloadSize);
AParcel_setDataPosition(dest, lastPosition);
return totalPayloadSize;
}
bool LargeParcelableBase::hasDeserializedParcelable() const {
return mHasDeserializedParcelable;
}
binder_status_t LargeParcelableBase::getParcelFromMemoryFile(const ScopedFileDescriptor& fd,
AParcel* parcel) {
borrowed_fd memoryFd = scopedFdToBorrowedFd(fd);
SharedMemory sharedMemory(memoryFd);
if (!sharedMemory.isValid()) {
ALOGE("invalid shared memory fd, status: %d", sharedMemory.getErr());
return STATUS_FDS_NOT_ALLOWED;
}
if (binder_status_t status = copyFromSharedMemory(sharedMemory, parcel); status != STATUS_OK) {
ALOGE("failed to copy from shared memory: %d", status);
return status;
}
return STATUS_OK;
}
binder_status_t LargeParcelableBase::parcelToMemoryFile(const AParcel& parcel,
ScopedFileDescriptor* sharedMemoryFd) {
int32_t payloadSize = AParcel_getDataPosition(&parcel);
binder_status_t status = STATUS_OK;
std::unique_ptr<SharedMemory> sharedMemory =
serializeParcelToSharedMemory(parcel, /*start=*/0, payloadSize, &status);
if (status != STATUS_OK) {
ALOGE("failed to serialize parcel to shared memory: %d", status);
return status;
}
unique_fd fd(sharedMemory->getDupFd());
sharedMemoryFd->set(fd.release());
return STATUS_OK;
}
binder_status_t LargeParcelableBase::serializeMemoryFdOrPayload(
AParcel* dest, const SharedMemory* sharedMemory) const {
// This is compatible with stable AIDL serialization:
// payload size + payload + nullable fd
// The shared Memory file might contain a serialized parcel created from this function.
int32_t startPosition = AParcel_getDataPosition(dest);
AParcel_writeInt32(dest, 0);
if (sharedMemory == nullptr) {
if (binder_status_t status = serialize(dest); status != STATUS_OK) {
ALOGE("failed to serialize: %d", status);
return status;
}
} else {
serializeNullPayload(dest);
}
if (DBG_PAYLOAD) {
int sharedMemoryPosition = AParcel_getDataPosition(dest) - startPosition;
ALOGD("Serialize shared memory fd: sharedMemoryPosition:%d hasSharedMemory:%d",
sharedMemoryPosition, sharedMemory != nullptr);
}
if (binder_status_t status = writeSharedMemoryCompatibleToParcel(sharedMemory, dest);
status != STATUS_OK) {
ALOGE("failed to write file descriptor to parcel: %d", status);
return status;
}
updatePayloadSize(dest, startPosition);
return STATUS_OK;
}
} // namespace car_binder_lib
} // namespace automotive
} // namespace android