blob: 9ecca7497ea328faa0e1e7fb9fa368c488cf44b1 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libprotobinder/parcel.h"
#include <string.h>
#include "libprotobinder/binder.pb.h"
#include "libprotobinder/binder_host.h"
#include "libprotobinder/binder_proxy.h"
#define PAD_SIZE(s) (((s) + 3) & ~3)
namespace protobinder {
Parcel::Parcel()
: data_(nullptr),
data_len_(0),
data_capacity_(0),
data_pos_(0),
objects_(nullptr),
objects_count_(0),
objects_capacity_(0) {
}
Parcel::~Parcel() {
if (!release_callback_.is_null()) {
release_callback_.Run(this);
} else {
if (data_)
free(data_);
if (objects_)
free(objects_);
}
}
// ---- Public API ----
bool Parcel::WriteInt32(int32_t val) {
return WriteAligned(val);
}
bool Parcel::WriteInt64(int64_t val) {
return WriteAligned(val);
}
bool Parcel::WriteUInt32(uint32_t val) {
return WriteAligned(val);
}
bool Parcel::WriteUInt64(uint64_t val) {
return WriteAligned(val);
}
bool Parcel::WriteFloat(float val) {
return WriteAligned(val);
}
bool Parcel::WriteDouble(double val) {
return WriteAligned(val);
}
bool Parcel::WritePointer(uintptr_t val) {
return WriteAligned<binder_uintptr_t>(val);
}
bool Parcel::ReadInt32(int32_t* val) {
return ReadAligned(val);
}
bool Parcel::ReadInt64(int64_t* val) {
return ReadAligned(val);
}
bool Parcel::ReadUInt32(uint32_t* val) {
return ReadAligned(val);
}
bool Parcel::ReadUInt64(uint64_t* val) {
return ReadAligned(val);
}
bool Parcel::ReadFloat(float* val) {
return ReadAligned(val);
}
bool Parcel::ReadDouble(double* val) {
return ReadAligned(val);
}
bool Parcel::ReadPointer(uintptr_t* val) {
binder_uintptr_t pointer_val = 0;
if (ReadAligned(&pointer_val)) {
*val = pointer_val;
return true;
}
return false;
}
bool Parcel::Write(const void* data, size_t len) {
void* buf = AllocatePaddedBuffer(len);
if (buf == nullptr)
return false;
memcpy(buf, data, len);
return true;
}
bool Parcel::WriteString16(const uint16_t* str, size_t len) {
// Only 32bit lengths are supported.
if (static_cast<uint64_t>(len) >= 0x100000000)
return false;
size_t alloc_len = len * sizeof(uint16_t);
if (alloc_len < len)
return false;
if (!WriteUInt32(len))
return false;
return Write(str, alloc_len);
}
bool Parcel::WriteString16(const std::string& str) {
size_t len = str.size();
// Only 32bit lengths are supported.
if (static_cast<uint64_t>(len) >= 0x100000000)
return false;
// Android String16 Parcel code expects a NULL byte
size_t alloc_len = (len + 1) * sizeof(uint16_t);
if (alloc_len <= len)
return false;
if (!WriteUInt32(len))
return false;
uint16_t* buf = reinterpret_cast<uint16_t*>(AllocatePaddedBuffer(alloc_len));
if (buf == nullptr)
return false;
for (size_t i = 0; i < len; i++)
buf[i] = str[i];
buf[len] = '\0';
return true;
}
bool Parcel::WriteString(const std::string& str) {
size_t len = str.size();
// Only 32bit lengths are supported.
if (static_cast<uint64_t>(len) >= 0x100000000)
return false;
if (!WriteUInt32(len))
return false;
// No need to store NULL
return Write(str.c_str(), len);
}
bool Parcel::Read(void* data, size_t len) {
if ((data_pos_ + PAD_SIZE(len)) >= data_pos_ &&
(data_pos_ + PAD_SIZE(len)) <= data_len_ && len <= PAD_SIZE(len)) {
memcpy(data, data_ + data_pos_, len);
data_pos_ += PAD_SIZE(len);
return true;
}
return false;
}
// TODO(leecam): Replace most of these nullptr checks with a DCHECK().
// http://brbug.com/599
bool Parcel::ReadString16(uint16_t* new_string, size_t* max_len) {
if (new_string == nullptr || max_len == nullptr)
return false;
uint32_t len = 0;
if (!ReadUInt32(&len))
return false;
if (len > *max_len)
return false;
size_t alloc_len = len * sizeof(uint16_t);
if (alloc_len < len)
return false;
uint16_t* buf = reinterpret_cast<uint16_t*>(GetPaddedBuffer(alloc_len));
if (buf == nullptr)
return false;
memcpy(new_string, buf, alloc_len);
*max_len = len;
return true;
}
bool Parcel::ReadString16(std::string* new_string) {
if (new_string == nullptr)
return false;
uint32_t len = 0;
if (!ReadUInt32(&len))
return false;
size_t alloc_len = (len + 1) * sizeof(uint16_t);
if (alloc_len <= len)
return false;
uint16_t* buf = reinterpret_cast<uint16_t*>(GetPaddedBuffer(alloc_len));
if (buf == nullptr)
return false;
new_string->clear();
for (size_t i = 0; i < len; i++)
new_string->push_back(buf[i]);
return true;
}
bool Parcel::ReadString(std::string* new_string) {
if (new_string == nullptr)
return false;
uint32_t len = 0;
if (!ReadUInt32(&len))
return false;
char* buf = reinterpret_cast<char*>(GetPaddedBuffer(len));
if (buf == nullptr)
return false;
new_string->clear();
new_string->insert(0, buf, len);
return true;
}
bool Parcel::WriteStrongBinderFromProtocolBuffer(const StrongBinder& binder) {
flat_binder_object object;
object.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
object.type = BINDER_TYPE_BINDER;
object.binder = 0;
object.handle = 0;
object.cookie = 0;
if (binder.has_host_cookie()) {
object.type = BINDER_TYPE_BINDER;
object.binder = static_cast<binder_uintptr_t>(binder.host_cookie());
object.cookie = object.binder;
} else if (binder.has_proxy_handle()) {
object.type = BINDER_TYPE_HANDLE;
object.handle = binder.proxy_handle();
}
return WriteObject(object);
}
bool Parcel::WriteStrongBinderFromIBinder(const IBinder& binder) {
flat_binder_object object;
object.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
object.type = BINDER_TYPE_BINDER;
object.binder = 0;
object.handle = 0;
object.cookie = 0;
if (binder.GetBinderHost()) {
object.type = BINDER_TYPE_BINDER;
object.binder =
static_cast<binder_uintptr_t>(binder.GetBinderHost()->cookie());
object.cookie = object.binder;
} else if (binder.GetBinderProxy()) {
object.type = BINDER_TYPE_HANDLE;
object.handle = binder.GetBinderProxy()->handle();
} else {
LOG(FATAL) << "IBinder is neither host nor proxy";
}
return WriteObject(object);
}
bool Parcel::WriteFd(int fd) {
struct flat_binder_object object;
object.type = BINDER_TYPE_FD;
object.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
object.handle = fd;
object.cookie = 0;
return WriteObject(object);
}
bool Parcel::WriteRawHandle(uint32_t handle) {
struct flat_binder_object object;
object.type = BINDER_TYPE_HANDLE;
object.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
object.handle = handle;
object.cookie = 0;
return WriteObject(object);
}
bool Parcel::WriteRawBinder(const void* binder) {
struct flat_binder_object object;
object.type = BINDER_TYPE_BINDER;
object.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
object.binder = reinterpret_cast<binder_uintptr_t>(binder);
object.cookie = 0;
return WriteObject(object);
}
bool Parcel::ReadStrongBinderToIBinder(IBinder** binder) {
if (!binder)
return false;
const flat_binder_object* flat_object = ReadObject();
if (!flat_object)
return false;
switch (flat_object->type) {
case BINDER_TYPE_BINDER:
// TODO(leecam): Support this and return flat->cookie
return false;
case BINDER_TYPE_HANDLE:
*binder = new BinderProxy(flat_object->handle);
return true;
}
return false;
}
bool Parcel::ReadFd(int* fd) {
if (fd == nullptr)
return false;
const flat_binder_object* flat_object = ReadObject();
if (flat_object == nullptr)
return false;
if (flat_object->type != BINDER_TYPE_FD)
return false;
*fd = flat_object->handle;
return true;
}
bool Parcel::ReadRawBinder(void** binder) {
if (binder == nullptr)
return false;
const flat_binder_object* flat_object = ReadObject();
if (flat_object == nullptr)
return false;
if (flat_object->type != BINDER_TYPE_BINDER)
return false;
*binder = reinterpret_cast<void*>(flat_object->binder);
return true;
}
bool Parcel::ReadRawHandle(uint32_t* handle) {
if (handle == nullptr)
return false;
const flat_binder_object* flat_object = ReadObject();
if (flat_object == nullptr)
return false;
if (flat_object->type != BINDER_TYPE_HANDLE)
return false;
*handle = flat_object->handle;
return true;
}
bool Parcel::WriteParcel(Parcel* parcel) {
if (parcel == nullptr)
return false;
size_t required_object_count = objects_count_ + parcel->ObjectCount();
if (required_object_count < objects_count_)
return false;
if (required_object_count >= objects_capacity_) {
size_t new_capacity = required_object_count + 16;
if (new_capacity < required_object_count)
return false;
binder_size_t* new_objects = reinterpret_cast<binder_size_t*>(
realloc(objects_, new_capacity * sizeof(binder_size_t)));
if (new_objects == nullptr)
return false;
objects_ = new_objects;
objects_capacity_ = new_capacity;
}
size_t base = data_pos_;
if (!Write(parcel->Data(), parcel->Len()))
return false;
for (size_t i = 0; i < parcel->ObjectCount(); i++) {
objects_[objects_count_] = base + parcel->ObjectData()[i];
objects_count_++;
}
return true;
}
bool Parcel::GetFdAtOffset(int* fd, size_t offset) {
if (fd == nullptr)
return false;
const flat_binder_object* flat_object = GetObjectAtOffset(offset);
if (flat_object == nullptr)
return false;
if (flat_object->type != BINDER_TYPE_FD)
return false;
*fd = flat_object->handle;
return true;
}
bool Parcel::CopyStrongBinderAtOffsetToProtocolBuffer(size_t offset,
StrongBinder* proto) {
CHECK(proto);
proto->Clear();
const flat_binder_object* flat_object = GetObjectAtOffset(offset);
if (!flat_object)
return false;
switch (flat_object->type) {
case BINDER_TYPE_BINDER:
proto->set_host_cookie(flat_object->cookie);
return true;
case BINDER_TYPE_HANDLE:
proto->set_proxy_handle(flat_object->handle);
return true;
}
return false;
}
bool Parcel::InitFromBinderTransaction(
void* data,
size_t data_len,
binder_size_t* objects,
size_t objects_size,
const ReleaseCallback& release_callback) {
if (data_) {
// Already allocated.
return false;
}
data_ = reinterpret_cast<uint8_t*>(data);
data_len_ = data_len;
objects_ = objects;
objects_count_ = objects_size / sizeof(binder_size_t);
release_callback_ = release_callback;
return true;
}
bool Parcel::SetCapacity(size_t capacity) {
// Can't shink yet
if (capacity < data_capacity_)
return false;
size_t new_capacity = PAD_SIZE(capacity);
if (new_capacity < capacity)
return false;
uint8_t* new_data = reinterpret_cast<uint8_t*>(realloc(data_, new_capacity));
if (new_data == nullptr)
return false;
data_ = new_data;
data_capacity_ = new_capacity;
return true;
}
bool Parcel::SetLen(size_t len) {
if (len > data_capacity_)
return false;
data_len_ = len;
return true;
}
bool Parcel::SetPos(size_t pos) {
if (pos > data_len_)
return false;
data_pos_ = pos;
return true;
}
// ---- Private API ----
template <class T>
bool Parcel::WriteAligned(T val) {
// TODO(leecam): compile time check for alignment.
if (data_pos_ + sizeof(val) < data_pos_)
return false;
if (data_pos_ + sizeof(val) > data_capacity_) {
// Grow the backing buffer.
if (!Grow(sizeof(val)))
return false;
}
*reinterpret_cast<T*>(data_ + data_pos_) = val;
AdvancePostion(sizeof(val));
return true;
}
bool Parcel::WriteObject(const flat_binder_object& object) {
if (data_pos_ + sizeof(object) < data_pos_)
return false;
if (data_pos_ + sizeof(object) > data_capacity_) {
// Need to grow data
if (!Grow(sizeof(object)))
return false;
}
if (objects_count_ >= objects_capacity_) {
// Need to grow objects.
// TODO(leecam): Is this growth strategy any good?
size_t new_capacity = objects_capacity_ + 16;
if (new_capacity < objects_capacity_)
return false;
binder_size_t* new_objects = reinterpret_cast<binder_size_t*>(
realloc(objects_, new_capacity * sizeof(binder_size_t)));
if (new_objects == nullptr)
return false;
objects_ = new_objects;
objects_capacity_ = new_capacity;
}
// Now have enough room to add the object.
*reinterpret_cast<flat_binder_object*>(data_ + data_pos_) = object;
objects_[objects_count_] = data_pos_;
objects_count_++;
AdvancePostion(sizeof(object));
return true;
}
template <class T>
bool Parcel::ReadAligned(T* val) {
// TODO(leecam): Compile time check for aligmment.
if (data_pos_ + sizeof(T) < data_pos_)
return false;
if (val == nullptr)
return false;
if ((data_pos_ + sizeof(T)) <= data_len_) {
const void* data = data_ + data_pos_;
data_pos_ += sizeof(T);
*val = *reinterpret_cast<const T*>(data);
return true;
}
return false;
}
const flat_binder_object* Parcel::ReadObject() {
if (data_pos_ + sizeof(flat_binder_object) < data_pos_)
return nullptr;
if ((data_pos_ + sizeof(flat_binder_object)) > data_len_)
return nullptr;
const flat_binder_object* obj =
reinterpret_cast<const flat_binder_object*>(data_ + data_pos_);
// TODO(leecam): Validate this object.
data_pos_ += sizeof(flat_binder_object);
return obj;
}
bool Parcel::Grow(size_t extra_required) {
// Increase capacity by |extra_required| then add 50%
// to save on repeated allocations.
size_t new_capacity = PAD_SIZE(((data_capacity_ + extra_required) * 3) / 2);
if (new_capacity < data_capacity_)
return false;
return SetCapacity(new_capacity);
}
void Parcel::AdvancePostion(size_t len) {
// Overflow and capacity checks always done in caller.
data_pos_ += len;
if (data_pos_ > data_len_)
data_len_ = data_pos_;
}
void* Parcel::AllocatePaddedBuffer(size_t len) {
size_t padded_len = PAD_SIZE(len);
if (padded_len < len || (data_pos_ + padded_len) < data_pos_)
return nullptr;
if (data_pos_ + padded_len >= data_capacity_) {
if (!Grow(padded_len))
return nullptr;
}
uint8_t* data = data_ + data_pos_;
// If we have padding then zero the last word.
if (padded_len > len) {
*reinterpret_cast<uint32_t*>(data + padded_len - sizeof(uint32_t)) = 0;
}
AdvancePostion(padded_len);
return data;
}
void* Parcel::GetPaddedBuffer(size_t len) {
if ((data_pos_ + PAD_SIZE(len)) >= data_pos_ &&
(data_pos_ + PAD_SIZE(len)) <= data_len_ && len <= PAD_SIZE(len)) {
void* data = data_ + data_pos_;
data_pos_ += PAD_SIZE(len);
return data;
}
return nullptr;
}
const flat_binder_object* Parcel::GetObjectAtOffset(size_t offset) {
size_t base = data_pos_ + (offset * sizeof(flat_binder_object));
if (base < (offset * sizeof(flat_binder_object)))
return nullptr;
if (base + sizeof(flat_binder_object) < data_pos_)
return nullptr;
if ((base + sizeof(flat_binder_object)) > data_len_)
return nullptr;
const flat_binder_object* obj =
reinterpret_cast<const flat_binder_object*>(data_ + base);
// TODO(leecam): Validate this object
return obj;
}
} // namespace protobinder