diff options
author | 2024-04-30 15:07:13 -0700 | |
---|---|---|
committer | 2024-08-07 09:16:49 -0700 | |
commit | d5fc446767fd6bed359710b7bae982334f0259bd (patch) | |
tree | ff64a77eaaa0ffc6fb58f9f5836bedc56a253fa5 | |
parent | ac728795d40213355f5b3e33006e6ace4b47d9ae (diff) |
Binder API for freeze state change notification.
Bug: 338097747
Change-Id: Iac340abc7a1a0700148cded9adb0451b8a4eae73
Test: atest BinderLibTest
-rw-r--r-- | libs/binder/Binder.cpp | 16 | ||||
-rw-r--r-- | libs/binder/BpBinder.cpp | 121 | ||||
-rw-r--r-- | libs/binder/IPCThreadState.cpp | 114 | ||||
-rw-r--r-- | libs/binder/ProcessState.cpp | 50 | ||||
-rw-r--r-- | libs/binder/binder_module.h | 30 | ||||
-rw-r--r-- | libs/binder/include/binder/BpBinder.h | 27 | ||||
-rw-r--r-- | libs/binder/include/binder/IBinder.h | 57 | ||||
-rw-r--r-- | libs/binder/include/binder/IPCThreadState.h | 17 | ||||
-rw-r--r-- | libs/binder/include/binder/ProcessState.h | 1 | ||||
-rw-r--r-- | libs/binder/tests/Android.bp | 4 | ||||
-rw-r--r-- | libs/binder/tests/binderDriverInterfaceTest.cpp | 248 | ||||
-rw-r--r-- | libs/binder/tests/binderLibTest.cpp | 321 | ||||
-rw-r--r-- | libs/binder/tests/binderRpcUniversalTests.cpp | 16 |
13 files changed, 958 insertions, 64 deletions
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index c57c9cdd62..53bd08d420 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -143,6 +143,22 @@ status_t IBinder::getExtension(sp<IBinder>* out) { 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 6594aa6309..c43cab22fb 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -557,6 +557,123 @@ void BpBinder::sendObituary() } } +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 @@ void BpBinder::onLastStrongRef(const void* /*id*/) { 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 94f947fce1..3d58b52af8 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -89,26 +89,33 @@ static const char* kReturnStrings[] = { "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 @@ static const void* printReturnCommand(std::ostream& out, const void* _cmd) { 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 @@ static const void* printCommand(std::ostream& out, const void* _cmd) { 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 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) 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 @@ status_t IPCThreadState::executeCommand(int32_t cmd) 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 a42ede29a2..47fd38fdf1 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -57,6 +57,25 @@ const char* kDefaultDriver = "/dev/binder"; // ------------------------------------------------------------------------- +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 @@ bool ProcessState::isThreadPoolStarted() const { #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 b3a2d9ec28..65cdcd7735 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 d7f74c4152..c473483313 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 17248ce289..61bab3e71a 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -198,9 +198,18 @@ public: 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 @@ public: 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 09ab442c7d..9ef4e694dd 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -174,6 +174,8 @@ public: 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 @@ private: 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 021bd58e21..3d5b754ceb 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -133,6 +133,7 @@ public: 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 4c7684ccb7..e3fb3635a6 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -42,6 +42,9 @@ cc_test { defaults: ["binder_test_defaults"], header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], + shared_libs: [ + "libbinder", + ], test_suites: [ "device-tests", "vts", @@ -127,6 +130,7 @@ cc_test { "libbase", "libbinder", "liblog", + "libprocessgroup", "libutils", ], static_libs: [ diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index de9d42bac0..3b5239fffa 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 @@ TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { 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 5582a96ac1..8806fcc8a9 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 namespace std::chrono_literals; 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 @@ enum BinderLibTestTranscationCode { 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 @@ class BinderLibTestEnv : public ::testing::Environment { 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 @@ class BinderLibTest : public ::testing::Test { 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, NopTransactionClear) { } 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 @@ TEST_F(BinderLibTest, DeathNotificationThread) 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 @@ public: 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 @@ private: 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 f4807806ad..2cec243c38 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -323,6 +323,22 @@ TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { // 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({}); |