Fix hidl_handle/hidl_memory ownership semantics.
hidl_handles that are constructed from a native_handle_t
object never own a handle, unless explicitly requested by
a call to setTo().
Copying a hidl_handle does clone the enclosed native_handle_t
and consequently takes ownership of it.
As a consequence, hidl_memory no longer needs to keep
track of handle ownership; the default constructors
don't take ownership of the passed in native_handle_t,
requiring callers to clean the handles up when it is
done with them. This makes sense because in most cases,
hidl_memory is a very transient object just used for
IPC.
Test: hidl_test, libhidl_test
Bug: 33812533
Change-Id: Idf26434c6a1dc541d4b909ac1523dbf92f214e1a
diff --git a/base/HidlSupport.cpp b/base/HidlSupport.cpp
index 593380b..61db8cc 100644
--- a/base/HidlSupport.cpp
+++ b/base/HidlSupport.cpp
@@ -48,6 +48,105 @@
return tr;
}
+hidl_handle::hidl_handle() {
+ mHandle = nullptr;
+ mOwnsHandle = false;
+}
+
+hidl_handle::~hidl_handle() {
+ freeHandle();
+}
+
+hidl_handle::hidl_handle(const native_handle_t *handle) {
+ mHandle = handle;
+ mOwnsHandle = false;
+}
+
+// copy constructor.
+hidl_handle::hidl_handle(const hidl_handle &other) {
+ mOwnsHandle = false;
+ *this = other;
+}
+
+// move constructor.
+hidl_handle::hidl_handle(hidl_handle &&other) {
+ mOwnsHandle = false;
+ *this = std::move(other);
+}
+
+// assignment operators
+hidl_handle &hidl_handle::operator=(const hidl_handle &other) {
+ if (this == &other) {
+ return *this;
+ }
+ freeHandle();
+ if (other.mHandle != nullptr) {
+ mHandle = native_handle_clone(other.mHandle);
+ if (mHandle == nullptr) {
+ LOG(FATAL) << "Failed to clone native_handle in hidl_handle.";
+ }
+ mOwnsHandle = true;
+ } else {
+ mHandle = nullptr;
+ mOwnsHandle = false;
+ }
+ return *this;
+}
+
+hidl_handle &hidl_handle::operator=(const native_handle_t *native_handle) {
+ freeHandle();
+ mHandle = native_handle;
+ mOwnsHandle = false;
+ return *this;
+}
+
+hidl_handle &hidl_handle::operator=(hidl_handle &&other) {
+ if (this != &other) {
+ freeHandle();
+ mHandle = other.mHandle;
+ mOwnsHandle = other.mOwnsHandle;
+ other.mHandle = nullptr;
+ other.mOwnsHandle = false;
+ }
+ return *this;
+}
+
+void hidl_handle::setTo(native_handle_t* handle, bool shouldOwn) {
+ mHandle = handle;
+ mOwnsHandle = shouldOwn;
+}
+
+const native_handle_t* hidl_handle::operator->() const {
+ return mHandle;
+}
+
+// implicit conversion to const native_handle_t*
+hidl_handle::operator const native_handle_t *() const {
+ return mHandle;
+}
+
+// explicit conversion
+const native_handle_t *hidl_handle::getNativeHandle() const {
+ return mHandle;
+}
+
+void hidl_handle::freeHandle() {
+ if (mOwnsHandle && mHandle != nullptr) {
+ // This can only be true if:
+ // 1. Somebody called setTo() with shouldOwn=true, so we know the handle
+ // wasn't const to begin with.
+ // 2. Copy/assignment from another hidl_handle, in which case we have
+ // cloned the handle.
+ // 3. Move constructor from another hidl_handle, in which case the original
+ // hidl_handle must have been non-const as well.
+ native_handle_t *handle = const_cast<native_handle_t*>(
+ static_cast<const native_handle_t*>(mHandle));
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ mHandle = nullptr;
+ }
+}
+
static const char *const kEmptyString = "";
hidl_string::hidl_string()
diff --git a/base/include/hidl/HidlSupport.h b/base/include/hidl/HidlSupport.h
index 4a17c3e..3f7248c 100644
--- a/base/include/hidl/HidlSupport.h
+++ b/base/include/hidl/HidlSupport.h
@@ -72,57 +72,54 @@
// hidl_handle wraps a pointer to a native_handle_t in a hidl_pointer,
// so that it can safely be transferred between 32-bit and 64-bit processes.
+// The ownership semantics for this are:
+// 1) The conversion constructor and assignment operator taking a const native_handle_t*
+// do not take ownership of the handle; this is because these operations are usually
+// just done for IPC, and cloning by default is a waste of resources. If you want
+// a hidl_handle to take ownership, call setTo(handle, true /*shouldOwn*/);
+// 2) The copy constructor/assignment operator taking a hidl_handle *DO* take ownership;
+// that is because it's not intuitive that this class encapsulates a native_handle_t
+// which needs cloning to be valid; in particular, this allows constructs like this:
+// hidl_handle copy;
+// foo->someHidlCall([&](auto incoming_handle) {
+// copy = incoming_handle;
+// });
+// // copy and its enclosed file descriptors will remain valid here.
+// 3) The move constructor does what you would expect; it only owns the handle if the
+// original did.
struct hidl_handle {
- hidl_handle() {
- mHandle = nullptr;
- }
- ~hidl_handle() {
- }
+ hidl_handle();
+ ~hidl_handle();
- // copy constructors.
- hidl_handle(const native_handle_t *handle) {
- mHandle = handle;
- }
+ hidl_handle(const native_handle_t *handle);
- hidl_handle(const hidl_handle &other) {
- mHandle = other.mHandle;
- }
+ // copy constructor.
+ hidl_handle(const hidl_handle &other);
// move constructor.
- hidl_handle(hidl_handle &&other) {
- *this = std::move(other);
- }
+ hidl_handle(hidl_handle &&other);
// assignment operators
- hidl_handle &operator=(const hidl_handle &other) {
- mHandle = other.mHandle;
- return *this;
- }
+ hidl_handle &operator=(const hidl_handle &other);
- hidl_handle &operator=(const native_handle_t *native_handle) {
- mHandle = native_handle;
- return *this;
- }
+ hidl_handle &operator=(const native_handle_t *native_handle);
- hidl_handle &operator=(hidl_handle &&other) {
- mHandle = other.mHandle;
- other.mHandle = nullptr;
- return *this;
- }
+ hidl_handle &operator=(hidl_handle &&other);
- const native_handle_t* operator->() const {
- return mHandle;
- }
+ void setTo(native_handle_t* handle, bool shouldOwn = false);
+
+ const native_handle_t* operator->() const;
+
// implicit conversion to const native_handle_t*
- operator const native_handle_t *() const {
- return mHandle;
- }
+ operator const native_handle_t *() const;
+
// explicit conversion
- const native_handle_t *getNativeHandle() const {
- return mHandle;
- }
+ const native_handle_t *getNativeHandle() const;
private:
+ void freeHandle();
+
details::hidl_pointer<const native_handle_t> mHandle;
+ bool mOwnsHandle;
};
struct hidl_string {
@@ -214,15 +211,17 @@
// - as well as all of its cross-process dups() - remain opened.
struct hidl_memory {
- hidl_memory() : mOwnsHandle(false), mHandle(nullptr), mSize(0), mName("") {
+ hidl_memory() : mHandle(nullptr), mSize(0), mName("") {
}
/**
- * Creates a hidl_memory object and takes ownership of the handle.
+ * Creates a hidl_memory object, but doesn't take ownership of
+ * the passed in native_handle_t; callers are responsible for
+ * making sure the handle remains valid while this object is
+ * used.
*/
- hidl_memory(const hidl_string &name, const hidl_handle &handle, size_t size)
- : mOwnsHandle(true),
- mHandle(handle),
+ hidl_memory(const hidl_string &name, const native_handle_t *handle, size_t size)
+ : mHandle(handle),
mSize(size),
mName(name)
{}
@@ -235,15 +234,7 @@
// copy assignment
hidl_memory &operator=(const hidl_memory &other) {
if (this != &other) {
- cleanup();
-
- if (other.mHandle == nullptr) {
- mHandle = nullptr;
- mOwnsHandle = false;
- } else {
- mOwnsHandle = true;
- mHandle = native_handle_clone(other.mHandle);
- }
+ mHandle = other.mHandle;
mSize = other.mSize;
mName = other.mName;
}
@@ -254,7 +245,6 @@
// TODO move constructor/move assignment
~hidl_memory() {
- cleanup();
}
const native_handle_t* handle() const {
@@ -275,17 +265,9 @@
static const size_t kOffsetOfName;
private:
- bool mOwnsHandle;
hidl_handle mHandle;
size_t mSize;
hidl_string mName;
-
- void cleanup() {
- // TODO(b/33812533): native_handle_delete
- if (mOwnsHandle && mHandle != nullptr) {
- native_handle_close(mHandle);
- }
- }
};
////////////////////////////////////////////////////////////////////////////////