native_handle_t support.
- writeEmbeddedNativeHandle to write a native_handle_t*
embedded inside a buffer.
- Factored out writeEmbeddedBuffer to make clear the buffer
is pointed to from inside another buffer.
- readBuffer()/readEmbeddedBuffer()/readEmbeddedNativeHandle()
- Added writeNativeHandleNoDup()/readNativeHandleNoDup() to
avoid copying file descriptors around if the caller doesn't
need it.
- Moved the binder header modifications inside libhwbinder
for now, until we can get them upstreamed and pulled down
to bionic's uapi headers again.
Change-Id: I3a2c44732f5dc9ed92363552b42aa3ff55a06a8d
diff --git a/IPCThreadState.cpp b/IPCThreadState.cpp
index 7c0dc0a..dda2064 100644
--- a/IPCThreadState.cpp
+++ b/IPCThreadState.cpp
@@ -927,37 +927,36 @@
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
- binder_transaction_data tr;
-
- tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
- tr.target.handle = handle;
- tr.code = code;
- tr.flags = binderFlags;
- tr.cookie = 0;
- tr.sender_pid = 0;
- tr.sender_euid = 0;
+ binder_transaction_data_sg tr_sg;
+ tr_sg.tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
+ tr_sg.tr.target.handle = handle;
+ tr_sg.tr.code = code;
+ tr_sg.tr.flags = binderFlags;
+ tr_sg.tr.cookie = 0;
+ tr_sg.tr.sender_pid = 0;
+ tr_sg.tr.sender_euid = 0;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
- tr.data_size = data.ipcDataSize();
- tr.data.ptr.buffer = data.ipcData();
- tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
- tr.data.ptr.offsets = data.ipcObjects();
- tr.buffers_size = data.ipcBufferSize();
+ tr_sg.tr.data_size = data.ipcDataSize();
+ tr_sg.tr.data.ptr.buffer = data.ipcData();
+ tr_sg.tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
+ tr_sg.tr.data.ptr.offsets = data.ipcObjects();
+ tr_sg.buffers_size = data.ipcBufferSize();
} else if (statusBuffer) {
- tr.flags |= TF_STATUS_CODE;
+ tr_sg.tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
- tr.data_size = sizeof(status_t);
- tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
- tr.offsets_size = 0;
- tr.data.ptr.offsets = 0;
- tr.buffers_size = 0;
+ tr_sg.tr.data_size = sizeof(status_t);
+ tr_sg.tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
+ tr_sg.tr.offsets_size = 0;
+ tr_sg.tr.data.ptr.offsets = 0;
+ tr_sg.buffers_size = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
- mOut.write(&tr, sizeof(tr));
+ mOut.write(&tr_sg, sizeof(tr_sg));
return NO_ERROR;
}
diff --git a/Parcel.cpp b/Parcel.cpp
index a8f1df2..b85f99d 100644
--- a/Parcel.cpp
+++ b/Parcel.cpp
@@ -168,6 +168,10 @@
}
return;
}
+ case BINDER_TYPE_PTR:
+ case BINDER_TYPE_FDA: {
+ return;
+ }
}
ALOGD("Invalid object type 0x%08x", obj.type);
@@ -223,6 +227,14 @@
}
return;
}
+ case BINDER_TYPE_PTR: {
+ // The relevant buffer is part of the transaction buffer and will be freed that way
+ return;
+ }
+ case BINDER_TYPE_FDA: {
+ // TODO free fdas
+ return;
+ }
}
ALOGE("Invalid object type 0x%08x", obj.type);
@@ -1353,18 +1365,18 @@
goto restart_write;
}
-status_t Parcel::writeBufferObject(const buffer_object& val)
+status_t Parcel::writeBufferObject(const binder_buffer_object& val)
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = mObjectsSize < mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
- *reinterpret_cast<buffer_object*>(mData+mDataPos) = val;
+ *reinterpret_cast<binder_buffer_object*>(mData+mDataPos) = val;
mObjects[mObjectsSize] = mDataPos;
mObjectsSize++;
- return finishWrite(sizeof(buffer_object));
+ return finishWrite(sizeof(binder_buffer_object));
}
if (!enoughData) {
@@ -1383,26 +1395,113 @@
goto restart_write;
}
-status_t Parcel::writeBuffer(void *buffer, size_t length, uint64_t *handle,
- uint64_t parent_handle, uint32_t parent_offset)
+status_t Parcel::writeEmbeddedBuffer(void *buffer, size_t length, uint64_t *handle,
+ uint64_t parent_buffer_handle, uint64_t parent_offset)
{
- buffer_object obj;
+ binder_buffer_object obj;
obj.type = BINDER_TYPE_PTR;
obj.buffer = reinterpret_cast<uintptr_t>(buffer);
obj.length = length;
+ obj.flags = BINDER_BUFFER_HAS_PARENT;
if (handle != nullptr) {
// We use an index into mObjects as a handle
*handle = mObjectsSize;
}
- if (parent_handle != UINT64_MAX) {
- // TODO verify parent
- obj.parent = parent_handle;
- obj.parent_offset = parent_offset; // TODO not safe on 32-bit
+ if (parent_buffer_handle < mObjectsSize) {
+ // TODO verify parent handle, parent itself
+ obj.parent = parent_buffer_handle;
+ obj.parent_offset = parent_offset; // TODO not safe on 32-bit binder, verify
}
// TODO we'll want to clean up object handling in general
return writeBufferObject(obj);
}
+status_t Parcel::writeBuffer(void *buffer, size_t length, uint64_t *handle)
+{
+ binder_buffer_object obj;
+ obj.type = BINDER_TYPE_PTR;
+ obj.buffer = reinterpret_cast<uintptr_t>(buffer);
+ obj.length = length;
+ obj.flags = 0;
+ if (handle != nullptr) {
+ // We use an index into mObjects as a handle
+ *handle = mObjectsSize;
+ }
+ // TODO we'll want to clean up object handling in general
+ return writeBufferObject(obj);
+}
+
+status_t Parcel::writeFileDescriptorArrayObject(const binder_fd_array_object& val)
+{
+ const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
+ const bool enoughObjects = mObjectsSize < mObjectsCapacity;
+ if (enoughData && enoughObjects) {
+restart_write:
+ *reinterpret_cast<binder_fd_array_object*>(mData+mDataPos) = val;
+
+ mObjects[mObjectsSize] = mDataPos;
+ mObjectsSize++;
+
+ return finishWrite(sizeof(binder_fd_array_object));
+ }
+
+ if (!enoughData) {
+ const status_t err = growData(sizeof(val));
+ if (err != NO_ERROR) return err;
+ }
+ if (!enoughObjects) {
+ size_t newSize = ((mObjectsSize+2)*3)/2;
+ if (newSize < mObjectsSize) return NO_MEMORY; // overflow
+ binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
+ if (objects == NULL) return NO_MEMORY;
+ mObjects = objects;
+ mObjectsCapacity = newSize;
+ }
+
+ goto restart_write;
+}
+
+status_t Parcel::writeNativeHandleNoDup(const native_handle_t *handle)
+{
+ struct binder_fd_array_object fd_array;
+ uint64_t buffer_handle;
+ // A native handle consists of a buffer with file desctiptors inside
+ size_t native_handle_size = sizeof(native_handle_t) +
+ handle->numFds * sizeof(int) + handle->numInts * sizeof(int);
+ status_t status = writeBuffer((void*) handle, native_handle_size, &buffer_handle);
+ if (status != OK) {
+ return status;
+ }
+ fd_array.type = BINDER_TYPE_FDA;
+ fd_array.flags = 0;
+ fd_array.num_fds = handle->numFds;
+ fd_array.parent = buffer_handle;
+ fd_array.parent_offset = offsetof(native_handle_t, data);
+ return (writeFileDescriptorArrayObject(fd_array));
+}
+
+status_t Parcel::writeEmbeddedNativeHandle(const native_handle_t *handle,
+ uint64_t parent_buffer_handle,
+ uint64_t parent_offset)
+{
+ struct binder_fd_array_object fd_array;
+ uint64_t buffer_handle;
+ // A native handle consists of a buffer with file desctiptors inside
+ size_t native_handle_size = sizeof(native_handle_t) +
+ handle->numFds * sizeof(int) + handle->numInts * sizeof(int);
+ status_t status = writeEmbeddedBuffer((void*) handle, native_handle_size, &buffer_handle,
+ parent_buffer_handle, parent_offset);
+ if (status != OK) {
+ return status;
+ }
+ fd_array.type = BINDER_TYPE_FDA;
+ fd_array.flags = 0;
+ fd_array.num_fds = handle->numFds;
+ fd_array.parent = buffer_handle;
+ fd_array.parent_offset = offsetof(native_handle_t, data);
+ return (writeFileDescriptorArrayObject(fd_array));
+}
+
status_t Parcel::writeNoException()
{
binder::Status status;
@@ -2038,7 +2137,6 @@
return h;
}
-
int Parcel::readFileDescriptor() const
{
const flat_binder_object* flat = readObject(true);
@@ -2156,7 +2254,25 @@
if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) {
const flat_binder_object* obj
= reinterpret_cast<const flat_binder_object*>(mData+DPOS);
- mDataPos = DPOS + sizeof(flat_binder_object);
+ // TODO need to make sure current type still fits in remaining data buffer
+ switch (obj->type) {
+ case BINDER_TYPE_WEAK_BINDER:
+ case BINDER_TYPE_WEAK_HANDLE:
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_FD: {
+ mDataPos = DPOS + sizeof(flat_binder_object);
+ break;
+ }
+ case BINDER_TYPE_PTR: {
+ mDataPos = DPOS + sizeof(binder_buffer_object);
+ break;
+ }
+ case BINDER_TYPE_FDA: {
+ mDataPos = DPOS + sizeof(binder_fd_array_object);
+ break;
+ }
+ }
if (!nullMetaData && (obj->cookie == 0 && obj->binder == 0)) {
// When transferring a NULL object, we don't write it into
// the object list, so we don't want to check for it when
@@ -2211,18 +2327,50 @@
return NULL;
}
-const void* Parcel::readBuffer() const
+const void* Parcel::readBuffer(uint64_t *buffer_handle) const
{
- const buffer_object* buffer_obj = (buffer_object*) readObject(true);
+ const binder_buffer_object* buffer_obj = (binder_buffer_object*) readObject(true);
// TODO we'll want to do a lot more verification, either here, or
// in a new verify method.
if (buffer_obj && buffer_obj->type == BINDER_TYPE_PTR) {
+ if (buffer_handle != nullptr) {
+ *buffer_handle = 0; // TODO fix this
+ }
return (void*)buffer_obj->buffer;
}
return nullptr;
}
+const void* Parcel::readEmbeddedBuffer(uint64_t *buffer_handle, uint64_t /*parent_buffer_handle*/,
+ uint64_t /*parent_offset*/) const
+{
+ // TODO verify parent and offset
+ return (readBuffer(buffer_handle));
+}
+
+const native_handle_t* Parcel::readEmbeddedNativeHandle(uint64_t /*parent_buffer_handle*/,
+ uint64_t /*parent_offset*/) const
+{
+ // TODO verify parent and offset, as well as fda object
+ return ((const native_handle_t*) readBuffer(nullptr));
+}
+
+const native_handle_t* Parcel::readNativeHandleNoDup() const
+{
+ native_handle_t *nat_handle = (native_handle_t*) readBuffer(nullptr);
+ if (nat_handle == nullptr) {
+ return nat_handle;
+ }
+ const binder_fd_array_object* fd_array_obj = (binder_fd_array_object*) readObject(true);
+ if (fd_array_obj && fd_array_obj->type == BINDER_TYPE_FDA) {
+ // TODO verification
+ return nat_handle;
+ }
+
+ return nullptr;
+}
+
void Parcel::closeFileDescriptors()
{
size_t i = mObjectsSize;
@@ -2267,8 +2415,8 @@
size_t i = mObjectsSize;
while (i > 0) {
i--;
- const buffer_object* buffer
- = reinterpret_cast<buffer_object*>(mData+mObjects[i]);
+ const binder_buffer_object* buffer
+ = reinterpret_cast<binder_buffer_object*>(mData+mObjects[i]);
if (buffer->type == BINDER_TYPE_PTR) {
dataSize += buffer->length;
}
diff --git a/include/hwbinder/Parcel.h b/include/hwbinder/Parcel.h
index 8ce6fcc..87178ce 100644
--- a/include/hwbinder/Parcel.h
+++ b/include/hwbinder/Parcel.h
@@ -27,8 +27,48 @@
#include <utils/String16.h>
#include <utils/Vector.h>
#include <utils/Flattenable.h>
+
#include <linux/binder.h>
+enum {
+ BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
+ BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
+};
+
+struct binder_transaction_data_sg {
+ binder_transaction_data tr; /* regular transaction data */
+ binder_size_t buffers_size; /* number of bytes of SG buffers */
+};
+
+struct binder_buffer_object {
+ __u32 type; /* MUST be BINDER_TYPE_PTR */
+ __u32 flags;
+
+ binder_uintptr_t buffer; /* The buffer to copy */
+ binder_size_t length; /* Length of the buffer to copy */
+ binder_size_t parent; /* index of parent in objects */
+ binder_size_t parent_offset; /* offset of pointer in parent */
+};
+
+struct binder_fd_array_object {
+ __u32 type; /* MUST be BINDER_TYPE_FDA */
+ __u32 flags;
+
+ binder_size_t num_fds;
+ binder_size_t parent; /* index of parent in objects */
+ binder_size_t parent_offset; /* offset of pointer in parent */
+};
+
+enum {
+ BINDER_BUFFER_HAS_PARENT = 0x01,
+};
+
+enum {
+ BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
+ BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
+};
+
+
#include <hwbinder/IInterface.h>
#include <hwbinder/Parcelable.h>
@@ -210,10 +250,16 @@
status_t writeDupImmutableBlobFileDescriptor(int fd);
status_t writeObject(const flat_binder_object& val, bool nullMetaData);
- status_t writeBufferObject(const buffer_object& val);
+ status_t writeBufferObject(const binder_buffer_object& val);
+ status_t writeFileDescriptorArrayObject(const binder_fd_array_object& val);
- status_t writeBuffer(void *buffer, size_t length, uint64_t *handle,
- uint64_t parent_handle = UINT64_MAX, uint32_t parent_offset = 0);
+ status_t writeBuffer(void *buffer, size_t length, uint64_t *handle);
+ status_t writeEmbeddedBuffer(void *buffer, size_t length, uint64_t *handle,
+ uint64_t parent_buffer_handle, uint64_t parent_offset);
+
+ status_t writeEmbeddedNativeHandle(const native_handle_t *handle,
+ uint64_t parent_buffer_handle, uint64_t parent_offset);
+ status_t writeNativeHandleNoDup(const native_handle* handle);
// Like Parcel.java's writeNoException(). Just writes a zero int32.
// Currently the native implementation doesn't do any of the StrictMode
// stack gathering and serialization that the Java implementation does.
@@ -343,7 +389,12 @@
const flat_binder_object* readObject(bool nullMetaData) const;
- const void* readBuffer() const;
+ const void* readBuffer(uint64_t *buffer_handle) const;
+ const void* readEmbeddedBuffer(uint64_t *buffer_handle,
+ uint64_t parent_buffer_handle, uint64_t parent_offset) const;
+ const native_handle_t* readEmbeddedNativeHandle(uint64_t parent_buffer_handle,
+ uint64_t parent_offset) const;
+ const native_handle_t* readNativeHandleNoDup() const;
// Explicitly close all file descriptors in the parcel.
void closeFileDescriptors();