371 lines
14 KiB
C++
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
|