Binder API for freeze state change notification.
Bug: 338097747
Change-Id: Iac340abc7a1a0700148cded9adb0451b8a4eae73
Test: atest BinderLibTest
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c57c9cd..53bd08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -143,6 +143,22 @@
return reply.readNullableStrongBinder(out);
}
+status_t IBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->addFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t IBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->removeFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
status_t IBinder::getDebugPid(pid_t* out) {
BBinder* local = this->localBinder();
if (local != nullptr) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 6594aa6..c43cab2 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -557,6 +557,123 @@
}
}
+status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "addFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+ LOG_ALWAYS_FATAL_IF(ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0,
+ "addFrozenStateChangeCallback on %s but there are no threads "
+ "(yet?) listening to incoming transactions. See "
+ "ProcessState::startThreadPool "
+ "and ProcessState::setThreadPoolMaxThreadCount. Generally you should "
+ "setup the binder threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ LOG_ALWAYS_FATAL_IF(callback == nullptr,
+ "addFrozenStateChangeCallback(): callback must be non-NULL");
+
+ const sp<FrozenStateChangeCallback> strongCallback = callback.promote();
+ if (strongCallback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ ALOGV("Requesting freeze notification: %p handle %d\n", this, binderHandle());
+ IPCThreadState* self = IPCThreadState::self();
+ status_t status = self->addFrozenStateChangeCallback(binderHandle(), this);
+ if (status != NO_ERROR) {
+ // Avoids logspam if kernel does not support freeze
+ // notification.
+ if (status != INVALID_OPERATION) {
+ ALOGE("IPCThreadState.addFrozenStateChangeCallback "
+ "failed with %s. %p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ return status;
+ }
+ mFrozen = std::make_unique<FrozenStateChange>();
+ if (!mFrozen) {
+ std::ignore =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ return NO_MEMORY;
+ }
+ }
+ if (mFrozen->initialStateReceived) {
+ strongCallback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ mFrozen->isFrozen
+ ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ }
+ ssize_t res = mFrozen->callbacks.add(callback);
+ if (res < 0) {
+ return res;
+ }
+ return NO_ERROR;
+ }
+}
+
+status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "removeFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ RpcMutexUniqueLock _l(mLock);
+
+ const size_t N = mFrozen ? mFrozen->callbacks.size() : 0;
+ for (size_t i = 0; i < N; i++) {
+ if (mFrozen->callbacks.itemAt(i) == callback) {
+ mFrozen->callbacks.removeAt(i);
+ if (mFrozen->callbacks.size() == 0) {
+ ALOGV("Clearing freeze notification: %p handle %d\n", this, binderHandle());
+ status_t status =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ if (status != NO_ERROR) {
+ ALOGE("Unexpected error from "
+ "IPCThreadState.removeFrozenStateChangeCallback: %s. "
+ "%p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ mFrozen.reset();
+ }
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+void BpBinder::onFrozenStateChanged(bool isFrozen) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(), "onFrozenStateChanged is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ ALOGV("Sending frozen state change notification for proxy %p handle %d, isFrozen=%s\n", this,
+ binderHandle(), isFrozen ? "true" : "false");
+
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ return;
+ }
+ bool stateChanged = !mFrozen->initialStateReceived || mFrozen->isFrozen != isFrozen;
+ if (stateChanged) {
+ mFrozen->isFrozen = isFrozen;
+ mFrozen->initialStateReceived = true;
+ for (size_t i = 0; i < mFrozen->callbacks.size();) {
+ sp<FrozenStateChangeCallback> callback = mFrozen->callbacks.itemAt(i).promote();
+ if (callback != nullptr) {
+ callback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ isFrozen ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ i++;
+ } else {
+ mFrozen->callbacks.removeItemsAt(i);
+ }
+ }
+ }
+}
+
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
@@ -686,6 +803,10 @@
if (ipc) ipc->clearDeathNotification(binderHandle(), this);
mObituaries = nullptr;
}
+ if (mFrozen != nullptr) {
+ std::ignore = IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), this);
+ mFrozen.reset();
+ }
mLock.unlock();
if (obits != nullptr) {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 94f947f..3d58b52 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -89,26 +89,33 @@
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_PENDING_FROZEN",
+ "BR_FROZEN_BINDER",
+ "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
};
-static const char *kCommandStrings[] = {
- "BC_TRANSACTION",
- "BC_REPLY",
- "BC_ACQUIRE_RESULT",
- "BC_FREE_BUFFER",
- "BC_INCREFS",
- "BC_ACQUIRE",
- "BC_RELEASE",
- "BC_DECREFS",
- "BC_INCREFS_DONE",
- "BC_ACQUIRE_DONE",
- "BC_ATTEMPT_ACQUIRE",
- "BC_REGISTER_LOOPER",
- "BC_ENTER_LOOPER",
- "BC_EXIT_LOOPER",
- "BC_REQUEST_DEATH_NOTIFICATION",
- "BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+static const char* kCommandStrings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
+ "BC_REQUEST_FREEZE_NOTIFICATION",
+ "BC_CLEAR_FREEZE_NOTIFICATION",
+ "BC_FREEZE_NOTIFICATION_DONE",
};
static const int64_t kWorkSourcePropagatedBitIndex = 32;
@@ -203,6 +210,18 @@
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BR_FROZEN_BINDER: {
+ const int32_t c = *cmd++;
+ const int32_t h = *cmd++;
+ const int32_t isFrozen = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c << " isFrozen: " << isFrozen;
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BR_OK, BR_DEAD_REPLY,
// BR_TRANSACTION_COMPLETE, BR_FINISHED
@@ -270,11 +289,23 @@
out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
+ case BC_REQUEST_FREEZE_NOTIFICATION:
+ case BC_CLEAR_FREEZE_NOTIFICATION: {
+ const int32_t h = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": handle=" << h << " (freeze cookie " << (void*)(uint64_t)c << ")";
+ } break;
+
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BC_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
// BC_EXIT_LOOPER
@@ -951,6 +982,33 @@
return NO_ERROR;
}
+status_t IPCThreadState::addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ proxy->getWeakRefs()->incWeak(proxy);
+ mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
+status_t IPCThreadState::removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ mOut.writeInt32(BC_CLEAR_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
@@ -1485,6 +1543,26 @@
proxy->getWeakRefs()->decWeak(proxy);
} break;
+ case BR_FROZEN_BINDER: {
+ const struct binder_frozen_state_info* data =
+ reinterpret_cast<const struct binder_frozen_state_info*>(
+ mIn.readInplace(sizeof(struct binder_frozen_state_info)));
+ if (data == nullptr) {
+ result = UNKNOWN_ERROR;
+ break;
+ }
+ BpBinder* proxy = (BpBinder*)data->cookie;
+ bool isFrozen = mIn.readInt32() > 0;
+ proxy->getPrivateAccessor().onFrozenStateChanged(data->is_frozen);
+ mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE);
+ mOut.writePointer(data->cookie);
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ BpBinder* proxy = (BpBinder*)mIn.readPointer();
+ proxy->getWeakRefs()->decWeak(proxy);
+ } break;
+
case BR_FINISHED:
result = TIMED_OUT;
break;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a42ede2..47fd38f 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -57,6 +57,25 @@
// -------------------------------------------------------------------------
+namespace {
+bool readDriverFeatureFile(const char* filename) {
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ char on;
+ if (fd == -1) {
+ ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, filename, strerror(errno));
+ return false;
+ }
+ if (read(fd, &on, sizeof(on)) == -1) {
+ ALOGE("%s: error reading to %s: %s", __func__, filename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return on == '1';
+}
+
+} // namespace
+
namespace android {
using namespace android::binder::impl;
@@ -472,27 +491,20 @@
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
- static const char* const names[] = {
- [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
- DRIVER_FEATURES_PATH "oneway_spam_detection",
- [static_cast<int>(DriverFeature::EXTENDED_ERROR)] =
- DRIVER_FEATURES_PATH "extended_error",
- };
- int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
- char on;
- if (fd == -1) {
- ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- return false;
+ // Use static variable to cache the results.
+ if (feature == DriverFeature::ONEWAY_SPAM_DETECTION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "oneway_spam_detection");
+ return enabled;
}
- if (read(fd, &on, sizeof(on)) == -1) {
- ALOGE("%s: error reading to %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- close(fd);
- return false;
+ if (feature == DriverFeature::EXTENDED_ERROR) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "extended_error");
+ return enabled;
}
- close(fd);
- return on == '1';
+ if (feature == DriverFeature::FREEZE_NOTIFICATION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "freeze_notification");
+ return enabled;
+ }
+ return false;
}
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index b3a2d9e..65cdcd7 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,4 +32,34 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
+struct binder_frozen_state_info {
+ binder_uintptr_t cookie;
+ __u32 is_frozen;
+};
+
+#ifndef BR_FROZEN_BINDER
+// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
+#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
+#endif // BR_FROZEN_BINDER
+
+#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
+#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
+
+#ifndef BC_REQUEST_FREEZE_NOTIFICATION
+// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
+#endif // BC_REQUEST_FREEZE_NOTIFICATION
+
+#ifndef BC_CLEAR_FREEZE_NOTIFICATION
+// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
+#endif // BC_CLEAR_FREEZE_NOTIFICATION
+
+#ifndef BC_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
+#endif // BC_FREEZE_NOTIFICATION_DONE
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index d7f74c4..c473483 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
// ---------------------------------------------------------------------------
namespace android {
+class IPCThreadState;
class RpcSession;
class RpcState;
namespace internal {
@@ -66,6 +67,12 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
@@ -75,7 +82,6 @@
LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
IBinder::object_make_func make,
const void* makeArgs);
-
LIBBINDER_EXPORTED virtual BpBinder* remoteBinder();
LIBBINDER_EXPORTED void sendObituary();
@@ -132,7 +138,10 @@
friend class ::android::ProcessState;
friend class ::android::RpcSession;
friend class ::android::RpcState;
- explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+ friend class ::android::IPCThreadState;
+ explicit PrivateAccessor(const BpBinder* binder)
+ : mBinder(binder), mMutableBinder(nullptr) {}
+ explicit PrivateAccessor(BpBinder* binder) : mBinder(binder), mMutableBinder(binder) {}
static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
@@ -146,12 +155,17 @@
uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
+ void onFrozenStateChanged(bool isFrozen) { mMutableBinder->onFrozenStateChanged(isFrozen); }
const BpBinder* mBinder;
+ BpBinder* mMutableBinder;
};
+
LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const {
return PrivateAccessor(this);
}
+ PrivateAccessor getPrivateAccessor() { return PrivateAccessor(this); }
+
private:
friend PrivateAccessor;
friend class sp<BpBinder>;
@@ -192,6 +206,14 @@
uint32_t flags;
};
+ void onFrozenStateChanged(bool isFrozen);
+
+ struct FrozenStateChange {
+ bool isFrozen = false;
+ Vector<wp<FrozenStateChangeCallback>> callbacks;
+ bool initialStateReceived = false;
+ };
+
void reportOneDeath(const Obituary& obit);
bool isDescriptorCached() const;
@@ -199,6 +221,7 @@
volatile int32_t mAlive;
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
+ std::unique_ptr<FrozenStateChange> mFrozen;
ObjectManager mObjects;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 17248ce..61bab3e 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -198,9 +198,18 @@
virtual void binderDied(const wp<IBinder>& who) = 0;
};
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
+ class FrozenStateChangeCallback : public virtual RefBase {
+ public:
+ enum class State {
+ FROZEN,
+ UNFROZEN,
+ };
+ virtual void onStateChanged(const wp<IBinder>& who, State state) = 0;
+ };
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
/**
* Register the @a recipient for a notification if this binder
@@ -249,6 +258,48 @@
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr) = 0;
+ /**
+ * addFrozenStateChangeCallback provides a callback mechanism to notify
+ * about process frozen/unfrozen events. Upon registration and any
+ * subsequent state changes, the callback is invoked with the latest process
+ * frozen state.
+ *
+ * If the listener process (the one using this API) is itself frozen, state
+ * change events might be combined into a single one with the latest state.
+ * (meaning 'frozen, unfrozen' might just be 'unfrozen'). This single event
+ * would then be delivered when the listener process becomes unfrozen.
+ * Similarly, if an event happens before the previous event is consumed,
+ * they might be combined. This means the callback might not be called for
+ * every single state change, so don't rely on this API to count how many
+ * times the state has changed.
+ *
+ * @note When all references to the binder are dropped, the callback is
+ * automatically removed. So, you must hold onto a binder in order to
+ * receive notifications about it.
+ *
+ * @note You will only receive freeze notifications for remote binders, as
+ * local binders by definition can't be frozen without you being frozen as
+ * well. Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This binder always holds a weak reference to the callback.
+ *
+ * @note You will only receive a weak reference to the binder object. You
+ * should not try to promote this to a strong reference. (Nor should you
+ * need to, as there is nothing useful you can directly do with it now that
+ * it has passed on.)
+ */
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
+ /**
+ * Remove a previously registered freeze callback.
+ * The @a callback will no longer be called if this object
+ * changes its frozen state.
+ */
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 09ab442..9ef4e69 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -174,6 +174,8 @@
LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder);
LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED static void shutdown();
@@ -210,13 +212,14 @@
IPCThreadState();
~IPCThreadState();
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
- status_t talkWithDriver(bool doReceive = true);
- status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code,
- const Parcel& data, status_t* statusBuffer);
- status_t getAndExecuteCommand();
- status_t executeCommand(int32_t command);
+ [[nodiscard]] status_t sendReply(const Parcel& reply, uint32_t flags);
+ [[nodiscard]] status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
+ [[nodiscard]] status_t talkWithDriver(bool doReceive = true);
+ [[nodiscard]] status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle,
+ uint32_t code, const Parcel& data,
+ status_t* statusBuffer);
+ [[nodiscard]] status_t getAndExecuteCommand();
+ [[nodiscard]] status_t executeCommand(int32_t command);
void processPendingDerefs();
void processPostWriteDerefs();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 021bd58..3d5b754 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -133,6 +133,7 @@
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
+ FREEZE_NOTIFICATION,
};
// Determine whether a feature is supported by the binder driver.
LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature);
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 4c7684c..e3fb363 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -42,6 +42,9 @@
defaults: ["binder_test_defaults"],
header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ ],
test_suites: [
"device-tests",
"vts",
@@ -127,6 +130,7 @@
"libbase",
"libbinder",
"liblog",
+ "libprocessgroup",
"libutils",
],
static_libs: [
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index de9d42b..3b5239f 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,12 +20,15 @@
#include <stdlib.h>
#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <linux/android/binder.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include "../binder_module.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -361,6 +364,251 @@
binderTestReadEmpty();
}
+TEST_F(BinderDriverInterfaceTest, RequestFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ // Expecting a BR_FROZEN_BINDER since BC_REQUEST_FREEZE_NOTIFICATION
+ // above should lead to an immediate notification of the current state.
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ binder_uintptr_t arg2;
+ uint32_t cmd3;
+ struct binder_handle_cookie arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc2 = {
+ // Tell kernel that userspace has done handling BR_FROZEN_BINDER.
+ .cmd2 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg2 = cookie,
+ .cmd3 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg3 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr2);
+ EXPECT_EQ(sizeof(bc2), bwr2.write_consumed);
+ EXPECT_EQ(sizeof(br2) - sizeof(br2.pad), bwr2.read_consumed);
+ EXPECT_EQ(BR_NOOP, br2.cmd2);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br2.cmd3);
+ EXPECT_EQ(cookie, br2.arg3);
+
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, OverwritePendingFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ uint32_t cmd3;
+ uint32_t arg3;
+ } __attribute__((packed)) bc = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ // This BC_REQUEST_FREEZE_NOTIFICATION should lead to a pending
+ // frozen notification inserted into the queue.
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ // Send BC_CLEAR_FREEZE_NOTIFICATION before the above frozen
+ // notification has a chance of being sent. The notification should
+ // be overwritten. Userspace is expected to only receive
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd3 = BC_DECREFS,
+ .arg3 = 0,
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc;
+ bwr.write_size = sizeof(bc);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed);
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br.cmd1);
+ EXPECT_EQ(cookie, br.arg1);
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, ResendFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ } __attribute__((packed)) bc2 = {
+ // Clear the notification before acknowledging the in-flight
+ // BR_FROZEN_BINDER. Kernel should hold off sending
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE until the acknowledgement
+ // reaches kernel.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+ struct {
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc3 = {
+ // Send the acknowledgement. Now the kernel should send out
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd3 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg3 = cookie,
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br3;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr2, EAGAIN);
+ binderTestReadEmpty();
+
+ struct binder_write_read bwr3 = binder_write_read();
+ bwr3.write_buffer = (uintptr_t)&bc3;
+ bwr3.write_size = sizeof(bc3);
+ bwr3.read_buffer = (uintptr_t)&br3;
+ bwr3.read_size = sizeof(br3);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr3);
+ EXPECT_EQ(sizeof(bc3), bwr3.write_consumed);
+ EXPECT_EQ(sizeof(br3) - sizeof(br3.pad), bwr3.read_consumed);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br3.cmd3);
+ EXPECT_EQ(cookie, br3.arg3);
+ binderTestReadEmpty();
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5582a96..8806fcc 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -27,6 +27,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <binder/Binder.h>
@@ -39,6 +40,8 @@
#include <binder/RpcSession.h>
#include <binder/Status.h>
#include <binder/unique_fd.h>
+#include <input/BlockingQueue.h>
+#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <linux/sched.h>
@@ -58,6 +61,7 @@
using android::base::testing::HasValue;
using android::binder::Status;
using android::binder::unique_fd;
+using std::chrono_literals::operator""ms;
using testing::ExplainMatchResult;
using testing::Matcher;
using testing::Not;
@@ -116,6 +120,8 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_GETUID,
+ BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE,
+ BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
@@ -249,6 +255,43 @@
sp<IBinder> m_server;
};
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ BlockingQueue<std::pair<const wp<IBinder>, State>> events;
+
+ virtual void onStateChanged(const wp<IBinder>& who, State state) {
+ events.push(std::make_pair(who, state));
+ }
+
+ void ensureFrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::FROZEN, event->second); // isFrozen should be true
+ EXPECT_EQ(0u, events.size());
+ }
+
+ void ensureUnfrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::UNFROZEN, event->second); // isFrozen should be false
+ EXPECT_EQ(0u, events.size());
+ }
+
+ std::vector<bool> getAllAndClear() {
+ std::vector<bool> results;
+ while (true) {
+ auto event = events.popWithTimeout(0ms);
+ if (!event.has_value()) {
+ break;
+ }
+ results.push_back(event->second == State::FROZEN);
+ }
+ return results;
+ }
+
+ sp<IBinder> binder;
+};
+
class BinderLibTest : public ::testing::Test {
public:
virtual void SetUp() {
@@ -293,6 +336,51 @@
EXPECT_EQ(1, ret);
}
+ bool checkFreezeSupport() {
+ std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ // Pass test on devices where the cgroup v2 freezer is not supported
+ if (freezer_file.fail()) {
+ return false;
+ }
+ return IPCThreadState::self()->freeze(getpid(), false, 0) == NO_ERROR;
+ }
+
+ bool checkFreezeAndNotificationSupport() {
+ if (!checkFreezeSupport()) {
+ return false;
+ }
+ return ProcessState::isDriverFeatureEnabled(
+ ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ }
+
+ bool getBinderPid(int32_t* pid, sp<IBinder> server) {
+ Parcel data, replypid;
+ if (server->transact(BINDER_LIB_TEST_GETPID, data, &replypid) != NO_ERROR) {
+ ALOGE("BINDER_LIB_TEST_GETPID failed");
+ return false;
+ }
+ *pid = replypid.readInt32();
+ if (*pid <= 0) {
+ ALOGE("pid should be greater than zero");
+ return false;
+ }
+ return true;
+ }
+
+ void freezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
+ }
+
+ void unfreezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
+ }
+
+ void removeCallbackAndValidateNoEvent(sp<IBinder> binder,
+ sp<TestFrozenStateChangeCallback> callback) {
+ EXPECT_THAT(binder->removeFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ EXPECT_EQ(0u, callback->events.size());
+ }
+
sp<IBinder> m_server;
};
@@ -518,29 +606,18 @@
}
TEST_F(BinderLibTest, Freeze) {
- Parcel data, reply, replypid;
- std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
-
- // Pass test on devices where the cgroup v2 freezer is not supported
- if (freezer_file.fail()) {
- GTEST_SKIP();
+ if (!checkFreezeSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support proceess freezing";
return;
}
-
+ Parcel data, reply, replypid;
EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
- // Pass test on devices where BINDER_FREEZE ioctl is not supported
- int ret = IPCThreadState::self()->freeze(pid, false, 0);
- if (ret == -EINVAL) {
- GTEST_SKIP();
- return;
- }
- EXPECT_EQ(NO_ERROR, ret);
-
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
// b/268232063 - succeeds ~0.08% of the time
@@ -837,6 +914,199 @@
EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, ReturnErrorIfKernelDoesNotSupportFreezeNotification) {
+ if (ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_EQ(nullptr, binder->localBinder());
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(INVALID_OPERATION));
+}
+
+TEST_F(BinderLibTest, FrozenStateChangeNotificatiion) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+}
+
+TEST_F(BinderLibTest, AddFrozenCallbackWhenFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ // Add the callback while the target process is frozen.
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ // Make sure no callback happens since the listener has been removed.
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, NoFrozenNotificationAfterCallbackRemoval) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Make sure no callback happens after the listener is removed.
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, MultipleFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback1 = sp<TestFrozenStateChangeCallback>::make();
+ sp<TestFrozenStateChangeCallback> callback2 = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback1), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback1->ensureUnfrozenEventReceived();
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback2), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback2->ensureUnfrozenEventReceived();
+
+ freezeProcess(pid);
+ callback1->ensureFrozenEventReceived();
+ callback2->ensureFrozenEventReceived();
+
+ removeCallbackAndValidateNoEvent(binder, callback1);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback1->events.size());
+ callback2->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback2);
+
+ freezeProcess(pid);
+ EXPECT_EQ(0u, callback2->events.size());
+}
+
+TEST_F(BinderLibTest, RemoveThenAddFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+}
+
+TEST_F(BinderLibTest, CoalesceFreezeCallbacksWhenListenerIsFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<IBinder> binder = addServer();
+ sp<IBinder> listener = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_NE(nullptr, listener);
+ int32_t pid, listenerPid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+ ASSERT_TRUE(getBinderPid(&listenerPid, listener));
+
+ // Ask the listener process to register for state change callbacks.
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(binder);
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, data,
+ &reply),
+ StatusEq(NO_ERROR));
+ }
+ // Freeze the listener process.
+ freezeProcess(listenerPid);
+ createProcessGroup(getuid(), listenerPid);
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Frozen"}));
+ // Repeatedly flip the target process between frozen and unfrozen states.
+ for (int i = 0; i < 1000; i++) {
+ usleep(50);
+ unfreezeProcess(pid);
+ usleep(50);
+ freezeProcess(pid);
+ }
+ // Unfreeze the listener process. Now it should receive the frozen state
+ // change notifications.
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Unfrozen"}));
+ unfreezeProcess(listenerPid);
+ // Wait for 500ms to give the process enough time to wake up and handle
+ // notifications.
+ usleep(500 * 1000);
+ {
+ std::vector<bool> events;
+ Parcel data, reply;
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, data, &reply),
+ StatusEq(NO_ERROR));
+ reply.readBoolVector(&events);
+ // There should only be one single state change notifications delievered.
+ ASSERT_EQ(1u, events.size());
+ EXPECT_TRUE(events[0]);
+ }
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -1983,6 +2253,26 @@
reply->writeInt32(param.sched_priority);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE: {
+ sp<IBinder> binder = data.readStrongBinder();
+ frozenStateChangeCallback = sp<TestFrozenStateChangeCallback>::make();
+ // Hold an strong pointer to the binder object so it doesn't go
+ // away.
+ frozenStateChangeCallback->binder = binder;
+ int ret = binder->addFrozenStateChangeCallback(frozenStateChangeCallback);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ if (!event.has_value()) {
+ return NOT_ENOUGH_DATA;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS: {
+ reply->writeBoolVector(frozenStateChangeCallback->getAllAndClear());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -2069,6 +2359,7 @@
sp<IBinder> m_callback;
bool m_exitOnDestroy;
std::mutex m_blockMutex;
+ sp<TestFrozenStateChangeCallback> frozenStateChangeCallback;
};
int run_server(int index, int readypipefd, bool usePoll)
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index f480780..2cec243 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -323,6 +323,22 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ virtual void onStateChanged(const wp<IBinder>&, State) {}
+};
+
+TEST_P(BinderRpc, RpcBinderShouldFailOnFrozenStateCallbacks) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ EXPECT_DEATH_IF_SUPPORTED(
+ { std::ignore = a->addFrozenStateChangeCallback(callback); },
+ "addFrozenStateChangeCallback\\(\\) is not supported for RPC Binder.");
+}
+
TEST_P(BinderRpc, RepeatRootObject) {
auto proc = createRpcTestSocketServerProcess({});