libbinder_ndk: linkToDeath/unlinkToDeath
This adds AIBinder_DeathRecipient and methods to use it in order
to receive death notifications.
Bug: 111445392
Test: ./ndk/runtests.sh
Test: manually killing a service and finding its death recipient
Change-Id: I556435cae88b01e6e2cfdc0a286bdb660859872c
diff --git a/libs/binder/ndk/AIBinder.cpp b/libs/binder/ndk/AIBinder.cpp
index 2219f8e..ee1d48a 100644
--- a/libs/binder/ndk/AIBinder.cpp
+++ b/libs/binder/ndk/AIBinder.cpp
@@ -22,6 +22,8 @@
#include <android-base/logging.h>
+using DeathRecipient = ::android::IBinder::DeathRecipient;
+
using ::android::IBinder;
using ::android::Parcel;
using ::android::sp;
@@ -179,6 +181,63 @@
return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact);
}
+void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
+ CHECK(who == mWho);
+
+ mOnDied(mCookie);
+ mWho = nullptr;
+}
+
+AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
+ : mOnDied(onDied) {
+ CHECK(onDied != nullptr);
+}
+
+binder_status_t AIBinder_DeathRecipient::linkToDeath(AIBinder* binder, void* cookie) {
+ CHECK(binder != nullptr);
+
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+
+ sp<TransferDeathRecipient> recipient =
+ new TransferDeathRecipient(binder->getBinder(), cookie, mOnDied);
+
+ binder_status_t status = binder->getBinder()->linkToDeath(recipient, cookie, 0 /*flags*/);
+ if (status != EX_NONE) {
+ return status;
+ }
+
+ mDeathRecipients.push_back(recipient);
+ return EX_NONE;
+}
+
+binder_status_t AIBinder_DeathRecipient::unlinkToDeath(AIBinder* binder, void* cookie) {
+ CHECK(binder != nullptr);
+
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+
+ for (auto it = mDeathRecipients.rbegin(); it != mDeathRecipients.rend(); ++it) {
+ sp<TransferDeathRecipient> recipient = *it;
+
+ if (recipient->getCookie() == cookie &&
+
+ recipient->getWho() == binder->getBinder()) {
+ mDeathRecipients.erase(it.base() - 1);
+
+ binder_status_t status =
+ binder->getBinder()->unlinkToDeath(recipient, cookie, 0 /*flags*/);
+ if (status != EX_NONE) {
+ LOG(ERROR) << __func__
+ << ": removed reference to death recipient but unlink failed.";
+ }
+ return status;
+ }
+ }
+
+ return -ENOENT;
+}
+
+// start of C-API methods
+
AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
if (clazz == nullptr) {
LOG(ERROR) << __func__ << ": Must provide class to construct local binder.";
@@ -218,6 +277,26 @@
return binder->getBinder()->pingBinder();
}
+binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie) {
+ if (binder == nullptr || recipient == nullptr) {
+ LOG(ERROR) << __func__ << ": Must provide binder and recipient.";
+ return EX_NULL_POINTER;
+ }
+
+ return recipient->linkToDeath(binder, cookie);
+}
+
+binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie) {
+ if (binder == nullptr || recipient == nullptr) {
+ LOG(ERROR) << __func__ << ": Must provide binder and recipient.";
+ return EX_NULL_POINTER;
+ }
+
+ return recipient->unlinkToDeath(binder, cookie);
+}
+
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
LOG(ERROR) << __func__ << ": on null binder";
@@ -347,3 +426,21 @@
return parcelStatus;
}
+
+AIBinder_DeathRecipient* AIBinder_DeathRecipient_new(
+ AIBinder_DeathRecipient_onBinderDied onBinderDied) {
+ if (onBinderDied == nullptr) {
+ LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter.";
+ return nullptr;
+ }
+ return new AIBinder_DeathRecipient(onBinderDied);
+}
+
+void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient** recipient) {
+ if (recipient == nullptr) {
+ return;
+ }
+
+ delete *recipient;
+ *recipient = nullptr;
+}
diff --git a/libs/binder/ndk/AIBinder_internal.h b/libs/binder/ndk/AIBinder_internal.h
index 23949bb..f4c3249 100644
--- a/libs/binder/ndk/AIBinder_internal.h
+++ b/libs/binder/ndk/AIBinder_internal.h
@@ -20,6 +20,8 @@
#include "AIBinder_internal.h"
#include <atomic>
+#include <mutex>
+#include <vector>
#include <binder/Binder.h>
#include <binder/IBinder.h>
@@ -108,3 +110,38 @@
// one.
const ::android::String16 mInterfaceDescriptor;
};
+
+// Ownership is like this (when linked to death):
+//
+// AIBinder_DeathRecipient -sp-> TransferDeathRecipient <-wp-> IBinder
+//
+// When the AIBinder_DeathRecipient is dropped, so are the actual underlying death recipients. When
+// the IBinder dies, only a wp to it is kept.
+struct AIBinder_DeathRecipient {
+ // One of these is created for every linkToDeath. This is to be able to recover data when a
+ // binderDied receipt only gives us information about the IBinder.
+ struct TransferDeathRecipient : ::android::IBinder::DeathRecipient {
+ TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie,
+ const AIBinder_DeathRecipient_onBinderDied& onDied)
+ : mWho(who), mCookie(cookie), mOnDied(onDied) {}
+
+ void binderDied(const ::android::wp<::android::IBinder>& who) override;
+
+ const ::android::wp<::android::IBinder>& getWho() { return mWho; }
+ void* getCookie() { return mCookie; }
+
+ private:
+ ::android::wp<::android::IBinder> mWho;
+ void* mCookie;
+ const AIBinder_DeathRecipient_onBinderDied& mOnDied;
+ };
+
+ AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
+ binder_status_t linkToDeath(AIBinder* binder, void* cookie);
+ binder_status_t unlinkToDeath(AIBinder* binder, void* cookie);
+
+private:
+ std::mutex mDeathRecipientsMutex;
+ std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients;
+ AIBinder_DeathRecipient_onBinderDied mOnDied;
+};
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index aa29437..8f6672f 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -84,6 +84,10 @@
* implementation (usually a callback) to transfer all ownership to a remote process and
* automatically be deleted when the remote process is done with it or dies. Other memory models are
* possible, but this is the standard one.
+ *
+ * If the process containing an AIBinder dies, it is possible to be holding a strong reference to
+ * an object which does not exist. In this case, transactions to this binder will return
+ * EX_DEAD_OBJECT. See also AIBinder_linkToDeath, AIBinder_unlinkToDeath, and AIBinder_isAlive.
*/
struct AIBinder;
typedef struct AIBinder AIBinder;
@@ -97,6 +101,12 @@
typedef struct AIBinder_Weak AIBinder_Weak;
/**
+ * Represents a handle on a death notification. See AIBinder_linkToDeath/AIBinder_unlinkToDeath.
+ */
+struct AIBinder_DeathRecipient;
+typedef struct AIBinder_DeathRecipient AIBinder_DeathRecipient;
+
+/**
* This is called whenever a new AIBinder object is needed of a specific class.
*
* These arguments are passed from AIBinder_new. The return value is stored and can be retrieved
@@ -163,6 +173,26 @@
binder_status_t AIBinder_ping(AIBinder* binder);
/**
+ * Registers for notifications that the associated binder is dead. The same death recipient may be
+ * associated with multiple different binders. If the binder is local, then no death recipient will
+ * be given (since if the local process dies, then no recipient will exist to recieve a
+ * transaction). The cookie is passed to recipient in the case that this binder dies and can be
+ * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath).
+ * This function may return a binder transaction failure. The cookie can be used both for
+ * identification and holding user data.
+ */
+binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie);
+
+/**
+ * Stops registration for the associated binder dying. Does not delete the recipient. This function
+ * may return a binder transaction failure and in case the death recipient cannot be found, it
+ * returns -ENOENT.
+ */
+binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie);
+
+/**
* This can only be called if a strong reference to this object already exists in process.
*/
void AIBinder_incStrong(AIBinder* binder);
@@ -254,4 +284,21 @@
*/
__attribute__((warn_unused_result)) AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder);
+/**
+ * This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath.
+ */
+typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie);
+
+/**
+ * Creates a new binder death recipient. This can be attached to multiple different binder objects.
+ */
+__attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecipient_new(
+ AIBinder_DeathRecipient_onBinderDied onBinderDied);
+
+/**
+ * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before
+ * calling this as these will all be automatically unlinked.
+ */
+void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient** recipient);
+
__END_DECLS
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
index 967789f..519db5c 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/main_client.cpp
@@ -16,6 +16,7 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <gtest/gtest.h>
#include <iface/iface.h>
@@ -45,6 +46,28 @@
AIBinder_decStrong(binder);
}
+void OnBinderDeath(void* cookie) {
+ LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie;
+}
+
+TEST(NdkBinder, LinkToDeath) {
+ ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications
+ ABinderProcess_startThreadPool();
+
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binder);
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
+ ASSERT_NE(nullptr, recipient);
+
+ EXPECT_EQ(EX_NONE, AIBinder_linkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(EX_NONE, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(-ENOENT, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+
+ AIBinder_DeathRecipient_delete(&recipient);
+ AIBinder_decStrong(binder);
+}
+
class MyTestFoo : public IFoo {
int32_t doubleNumber(int32_t in) override {
LOG(INFO) << "doubleNumber " << in;