/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SuspendControlService.h" #include "SystemSuspend.h" #include "SystemSuspendAidl.h" #include "WakeupList.h" using aidl::android::system::suspend::ISystemSuspend; using aidl::android::system::suspend::IWakeLock; using aidl::android::system::suspend::SystemSuspendAidl; using aidl::android::system::suspend::WakeLockType; using android::sp; using android::base::Result; using android::base::Socketpair; using android::base::unique_fd; using android::base::WriteStringToFd; using android::base::WriteStringToFile; using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; using android::hardware::Return; using android::hardware::Void; using android::system::suspend::BnSuspendCallback; using android::system::suspend::BnWakelockCallback; using android::system::suspend::ISuspendControlService; using android::system::suspend::internal::ISuspendControlServiceInternal; using android::system::suspend::internal::WakeLockInfo; using android::system::suspend::internal::WakeupInfo; using android::system::suspend::V1_0::readFd; using android::system::suspend::V1_0::SleepTimeConfig; using android::system::suspend::V1_0::SuspendControlService; using android::system::suspend::V1_0::SuspendControlServiceInternal; using android::system::suspend::V1_0::SuspendStats; using android::system::suspend::V1_0::SystemSuspend; using android::system::suspend::V1_0::TimestampType; using android::system::suspend::V1_0::WakeupList; using namespace std::chrono_literals; namespace android { static constexpr char kServiceName[] = "TestService"; static constexpr char kControlServiceName[] = "TestControlService"; static constexpr char kControlServiceInternalName[] = "TestControlServiceInternal"; static bool isReadBlocked(int fd, int timeout_ms = 20) { struct pollfd pfd { .fd = fd, .events = POLLIN, }; return poll(&pfd, 1, timeout_ms) == 0; } class SystemSuspendTest : public ::testing::Test { protected: static void registerTestService() { std::thread testService([] { configureRpcThreadpool(1, true /* callerWillJoin */); sp suspendControl = new SuspendControlService(); auto controlStatus = ::android::defaultServiceManager()->addService( android::String16(kControlServiceName), suspendControl); if (android::OK != controlStatus) { LOG(FATAL) << "Unable to register service " << kControlServiceName << controlStatus; } sp suspendControlInternal = new SuspendControlServiceInternal(); controlStatus = ::android::defaultServiceManager()->addService( android::String16(kControlServiceInternalName), suspendControlInternal); if (android::OK != controlStatus) { LOG(FATAL) << "Unable to register service " << kControlServiceInternalName << controlStatus; } // Create non-HW binder threadpool for SuspendControlService. sp ps{android::ProcessState::self()}; ps->startThreadPool(); wakeupReasonsFd = unique_fd(TEMP_FAILURE_RETRY(open(wakeupReasonsFile.path, O_CLOEXEC | O_RDONLY))); suspendTimeFd = unique_fd(TEMP_FAILURE_RETRY(open(suspendTimeFile.path, O_CLOEXEC | O_RDONLY))); systemSuspend = new SystemSuspend( std::move(wakeupCountFds[1]), std::move(stateFds[1]), unique_fd(-1) /*suspendStatsFd*/, 1 /* maxNativeStatsEntries */, unique_fd(-1) /* kernelWakelockStatsFd */, std::move(wakeupReasonsFd), std::move(suspendTimeFd), kSleepTimeConfig, suspendControl, suspendControlInternal); std::shared_ptr suspendAidl = ndk::SharedRefBase::make(systemSuspend.get()); auto aidlBinder = suspendAidl->asBinder(); AIBinder_forceDowngradeToLocalStability(aidlBinder.get()); auto aidlStatus = AServiceManager_addService(aidlBinder.get(), kServiceName); CHECK(aidlStatus == STATUS_OK); joinRpcThreadpool(); }); testService.detach(); } static void SetUpTestSuite() { Socketpair(SOCK_STREAM, &wakeupCountFds[0], &wakeupCountFds[1]); Socketpair(SOCK_STREAM, &stateFds[0], &stateFds[1]); wakeupCountFd = wakeupCountFds[0]; stateFd = stateFds[0]; registerTestService(); std::shared_ptr suspendService = ISystemSuspend::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(kServiceName))); ASSERT_NE(suspendService, nullptr) << "failed to get suspend service"; sp control = android::defaultServiceManager()->getService(android::String16(kControlServiceName)); ASSERT_NE(control, nullptr) << "failed to get the suspend control service"; sp controlService = interface_cast(control); sp controlInternal = android::defaultServiceManager()->getService( android::String16(kControlServiceInternalName)); ASSERT_NE(controlInternal, nullptr) << "failed to get the suspend control internal service"; sp controlServiceInternal = interface_cast(controlInternal); // Start auto-suspend. bool enabled = false; controlServiceInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_EQ(enabled, true) << "failed to start autosuspend"; } static void TearDownTestSuite() { unblockSystemSuspendFromWakeupCount(); systemSuspend->disableAutosuspend(); } public: virtual void SetUp() override { suspendService = ISystemSuspend::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(kServiceName))); ASSERT_NE(suspendService, nullptr) << "failed to get suspend service"; sp control = android::defaultServiceManager()->getService(android::String16(kControlServiceName)); ASSERT_NE(control, nullptr) << "failed to get the suspend control service"; controlService = interface_cast(control); sp controlInternal = android::defaultServiceManager()->getService( android::String16(kControlServiceInternalName)); ASSERT_NE(controlInternal, nullptr) << "failed to get the suspend control internal service"; controlServiceInternal = interface_cast(controlInternal); systemSuspend->enableAutosuspend(new BBinder()); // SystemSuspend HAL should not have written back to wakeupCountFd or stateFd yet. ASSERT_TRUE(isReadBlocked(wakeupCountFd)); ASSERT_TRUE(isReadBlocked(stateFd)); } virtual void TearDown() override { // Allow some time for the autosuspend loop to happen, if unblocked std::this_thread::sleep_for(100ms); if (!isReadBlocked(wakeupCountFd)) readFd(wakeupCountFd); if (!isReadBlocked(stateFd)) readFd(stateFd); ASSERT_TRUE(isReadBlocked(wakeupCountFd)); ASSERT_TRUE(isReadBlocked(stateFd)); } static void unblockSystemSuspendFromWakeupCount() { std::string wakeupCount = std::to_string(rand()); ASSERT_TRUE(WriteStringToFd(wakeupCount, wakeupCountFd)); } bool isSystemSuspendBlocked(int timeout_ms = 20) { // Allow some time for the autosuspend loop to happen, if unblocked std::this_thread::sleep_for(100ms); return isReadBlocked(stateFd, timeout_ms); } std::shared_ptr acquireWakeLock(const std::string& name = "TestLock") { std::shared_ptr wl = nullptr; auto status = suspendService->acquireWakeLock(WakeLockType::PARTIAL, name, &wl); return wl; } size_t getActiveWakeLockCount() { std::vector wlStats; controlServiceInternal->getWakeLockStats(&wlStats); return count_if(wlStats.begin(), wlStats.end(), [](auto entry) { return entry.isActive; }); } void checkLoop(int numIter) { for (int i = 0; i < numIter; i++) { // Mock value for /sys/power/wakeup_count. std::string wakeupCount = std::to_string(rand()); ASSERT_TRUE(WriteStringToFd(wakeupCount, wakeupCountFd)); ASSERT_EQ(readFd(wakeupCountFd), wakeupCount) << "wakeup count value written by SystemSuspend is not equal to value given to it"; ASSERT_EQ(readFd(stateFd), "mem") << "SystemSuspend failed to write correct sleep state."; } } void checkWakelockLoop(int numIter, const std::string name) { for (int i = 0; i < numIter; i++) { std::shared_ptr testLock = acquireWakeLock(name); testLock->release(); } } void suspendFor(std::chrono::milliseconds suspendTime, int numberOfSuspends) { std::string suspendStr = "0.001 " /* placeholder */ + std::to_string( std::chrono::duration_cast>(suspendTime).count()); ASSERT_TRUE(WriteStringToFile(suspendStr, suspendTimeFile.path)); checkLoop(numberOfSuspends); } void checkSleepTime(std::chrono::milliseconds expected) { // There is a race window where sleepTime can be checked in the tests, // before it is updated in autoSuspend while (!isReadBlocked(wakeupCountFd)) { } std::chrono::milliseconds actual = systemSuspend->getSleepTime(); ASSERT_EQ(actual.count(), expected.count()) << "incorrect sleep time"; } std::shared_ptr suspendService; sp controlService; sp controlServiceInternal; static sp systemSuspend; static unique_fd wakeupCountFds[2]; static unique_fd stateFds[2]; static unique_fd wakeupReasonsFd; static unique_fd suspendTimeFd; static int wakeupCountFd; static int stateFd; static TemporaryFile wakeupReasonsFile; static TemporaryFile suspendTimeFile; static constexpr SleepTimeConfig kSleepTimeConfig = { .baseSleepTime = 100ms, .maxSleepTime = 400ms, .sleepTimeScaleFactor = 1.9, .backoffThreshold = 1, .shortSuspendThreshold = 100ms, .failedSuspendBackoffEnabled = true, .shortSuspendBackoffEnabled = true, }; }; // SystemSuspendTest test suite resources sp SystemSuspendTest::systemSuspend; unique_fd SystemSuspendTest::wakeupCountFds[2]; unique_fd SystemSuspendTest::stateFds[2]; unique_fd SystemSuspendTest::wakeupReasonsFd; unique_fd SystemSuspendTest::suspendTimeFd; int SystemSuspendTest::wakeupCountFd; int SystemSuspendTest::stateFd; TemporaryFile SystemSuspendTest::wakeupReasonsFile; TemporaryFile SystemSuspendTest::suspendTimeFile; // Tests that autosuspend thread can only be enabled once. TEST_F(SystemSuspendTest, OnlyOneEnableAutosuspend) { bool enabled = false; controlServiceInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_EQ(enabled, false); } // Tests that autosuspend thread can only enabled again after its been disabled. TEST_F(SystemSuspendTest, EnableAutosuspendAfterDisableAutosuspend) { bool enabled = false; checkLoop(1); controlServiceInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_FALSE(enabled); systemSuspend->disableAutosuspend(); unblockSystemSuspendFromWakeupCount(); controlServiceInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_TRUE(enabled); } TEST_F(SystemSuspendTest, DisableAutosuspendBlocksSuspend) { checkLoop(1); systemSuspend->disableAutosuspend(); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); // Re-enable autosuspend bool enabled = false; controlServiceInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_TRUE(enabled); } // TODO: Clean up binder tokens after soaking new implementation /* TEST_F(SystemSuspendTest, BlockAutosuspendIfBinderIsDead) { class DeadBinder : public BBinder { android::status_t pingBinder() override { return android::UNKNOWN_ERROR; } }; auto token = sp::make(); systemSuspend->disableAutosuspend(); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); bool enabled = false; controlServiceInternal->enableAutosuspend(token, &enabled); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked(150)); } TEST_F(SystemSuspendTest, UnresponsiveClientDoesNotBlockAcquireRelease) { static std::mutex _lock; static std::condition_variable inPingBinderCondVar; static bool inPingBinder = false; class UnresponsiveBinder : public BBinder { android::status_t pingBinder() override { auto lock = std::unique_lock(_lock); inPingBinder = true; inPingBinderCondVar.notify_all(); // Block pingBinder until test finishes and releases its lock inPingBinderCondVar.wait(lock); return android::UNKNOWN_ERROR; } }; systemSuspend->disableAutosuspend(); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); auto token = sp::make(); bool enabled = false; controlServiceInternal->enableAutosuspend(token, &enabled); unblockSystemSuspendFromWakeupCount(); auto lock = std::unique_lock(_lock); // wait until pingBinder has been called. if (!inPingBinder) { inPingBinderCondVar.wait(lock); } // let pingBinder finish once we release the test lock inPingBinderCondVar.notify_all(); std::condition_variable wakeLockAcquired; std::thread( [this](std::condition_variable& wakeLockAcquired) { std::shared_ptr testLock = acquireWakeLock("testLock"); testLock->release(); wakeLockAcquired.notify_all(); }, std::ref(wakeLockAcquired)) .detach(); std::mutex _acquireReleaseLock; auto acquireReleaseLock = std::unique_lock(_acquireReleaseLock); bool timedOut = wakeLockAcquired.wait_for(acquireReleaseLock, 200ms) == std::cv_status::timeout; ASSERT_FALSE(timedOut); } */ TEST_F(SystemSuspendTest, AutosuspendLoop) { checkLoop(5); } // Tests that upon WakeLock destruction SystemSuspend HAL is unblocked. TEST_F(SystemSuspendTest, WakeLockDestructor) { { std::shared_ptr wl = acquireWakeLock(); ASSERT_NE(wl, nullptr); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); } ASSERT_FALSE(isSystemSuspendBlocked()); } // Tests that upon WakeLock::release() SystemSuspend HAL is unblocked. TEST_F(SystemSuspendTest, WakeLockRelease) { std::shared_ptr wl = acquireWakeLock(); ASSERT_NE(wl, nullptr); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); wl->release(); ASSERT_FALSE(isSystemSuspendBlocked()); } // Tests that multiple WakeLocks correctly block SystemSuspend HAL. TEST_F(SystemSuspendTest, MultipleWakeLocks) { { std::shared_ptr wl1 = acquireWakeLock(); ASSERT_NE(wl1, nullptr); ASSERT_TRUE(isSystemSuspendBlocked()); { std::shared_ptr wl2 = acquireWakeLock(); ASSERT_NE(wl2, nullptr); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); } ASSERT_TRUE(isSystemSuspendBlocked()); } ASSERT_FALSE(isSystemSuspendBlocked()); } // Tests that upon thread deallocation WakeLock is destructed and SystemSuspend HAL is unblocked. TEST_F(SystemSuspendTest, ThreadCleanup) { std::thread clientThread([this] { std::shared_ptr wl = acquireWakeLock(); ASSERT_NE(wl, nullptr); unblockSystemSuspendFromWakeupCount(); ASSERT_TRUE(isSystemSuspendBlocked()); }); clientThread.join(); ASSERT_FALSE(isSystemSuspendBlocked()); } // Test that binder driver correctly deallocates acquired WakeLocks, even if the client processs // is terminated without ability to do clean up. TEST_F(SystemSuspendTest, CleanupOnAbort) { ASSERT_EXIT( { std::shared_ptr wl = acquireWakeLock(); ASSERT_NE(wl, nullptr); std::abort(); }, ::testing::KilledBySignal(SIGABRT), ""); ASSERT_TRUE(isSystemSuspendBlocked()); unblockSystemSuspendFromWakeupCount(); // Timing of the wake lock clean-up after process death is scheduler-dependent. // Increase the timeout to avoid flakes. ASSERT_FALSE(isSystemSuspendBlocked(200)); } // Stress test acquiring/releasing WakeLocks. TEST_F(SystemSuspendTest, WakeLockStressTest) { // numThreads threads will acquire/release numLocks locks each. constexpr int numThreads = 10; constexpr int numLocks = 10000; std::thread tds[numThreads]; for (int i = 0; i < numThreads; i++) { tds[i] = std::thread([this] { for (int j = 0; j < numLocks; j++) { std::shared_ptr wl1 = acquireWakeLock(); std::shared_ptr wl2 = acquireWakeLock(); wl2->release(); } }); } for (int i = 0; i < numThreads; i++) { tds[i].join(); } ASSERT_EQ(getActiveWakeLockCount(), 0); } TEST_F(SystemSuspendTest, SuspendBackoffLongSuspendTest) { // Sleep time shall be set to base sleep time after a long suspend suspendFor(10000ms, 1); checkSleepTime(kSleepTimeConfig.baseSleepTime); } TEST_F(SystemSuspendTest, BackoffThresholdTest) { // Sleep time shall be set to base sleep time after a long suspend suspendFor(10000ms, 1); checkSleepTime(kSleepTimeConfig.baseSleepTime); // Sleep time shall back off after the configured backoff threshold std::chrono::milliseconds expectedSleepTime = std::chrono::round( kSleepTimeConfig.baseSleepTime * kSleepTimeConfig.sleepTimeScaleFactor); suspendFor(10ms, kSleepTimeConfig.backoffThreshold); checkSleepTime(kSleepTimeConfig.baseSleepTime); suspendFor(10ms, 1); checkSleepTime(expectedSleepTime); // Sleep time shall return to base sleep time after a long suspend suspendFor(10000ms, 1); checkSleepTime(kSleepTimeConfig.baseSleepTime); } TEST_F(SystemSuspendTest, SuspendBackoffMaxTest) { // Sleep time shall be set to base sleep time after a long suspend suspendFor(10000ms, 1); checkSleepTime(kSleepTimeConfig.baseSleepTime); // Sleep time shall be capped at the configured maximum suspendFor(10ms, 3 + kSleepTimeConfig.backoffThreshold); checkSleepTime(kSleepTimeConfig.maxSleepTime); // Sleep time shall return to base sleep time after a long suspend suspendFor(10000ms, 1); checkSleepTime(kSleepTimeConfig.baseSleepTime); } // Callbacks are passed around as sp<>. However, mock expectations are verified when mock objects // are destroyed, i.e. the test needs to control lifetime of the mock object. // MockCallbackImpl can be destroyed independently of its wrapper MockCallback which is passed to // SystemSuspend. struct MockCallbackImpl { binder::Status notifyWakeup([[maybe_unused]] bool success, const std::vector& wakeupReasons) { mWakeupReasons = wakeupReasons; mNumWakeups++; return binder::Status::ok(); } std::vector mWakeupReasons; int mNumWakeups = 0; }; class MockCallback : public BnSuspendCallback { public: MockCallback(MockCallbackImpl* impl) : mImpl(impl), mDisabled(false) {} binder::Status notifyWakeup(bool x, const std::vector& wakeupReasons) { return mDisabled ? binder::Status::ok() : mImpl->notifyWakeup(x, wakeupReasons); } // In case we pull the rug from under MockCallback, but SystemSuspend still has an sp<> to the // object. void disable() { mDisabled = true; } private: MockCallbackImpl* mImpl; bool mDisabled; }; // Tests that nullptr can't be registered as callbacks. TEST_F(SystemSuspendTest, RegisterInvalidCallback) { bool retval = false; controlService->registerCallback(nullptr, &retval); ASSERT_FALSE(retval); } // Tests that SystemSuspend HAL correctly notifies wakeup events. TEST_F(SystemSuspendTest, CallbackNotifyWakeup) { constexpr int numWakeups = 5; MockCallbackImpl impl; sp cb = new MockCallback(&impl); bool retval = false; controlService->registerCallback(cb, &retval); ASSERT_TRUE(retval); checkLoop(numWakeups + 1); cb->disable(); // SystemSuspend should suspend numWakeup + 1 times. However, it might // only be able to notify numWakeup times. The test case might have // finished by the time last notification completes. ASSERT_GE(impl.mNumWakeups, numWakeups); } // Tests that SystemSuspend HAL correctly notifies wakeup subscribers with wakeup reasons. TEST_F(SystemSuspendTest, CallbackNotifyWakeupReason) { int i; const std::string wakeupReason0 = ""; const std::string wakeupReason1 = " "; const std::string wakeupReason2 = "\n\n"; const std::string wakeupReason3 = "100 :android,wakeup-reason-1"; const std::string wakeupReason4 = "Abort: android,wakeup-reason-2\n"; const std::string wakeupReason5 = "999 :android,wakeup-reason-3\nAbort: android,wakeup-reason-3\n"; const std::string referenceWakeupUnknown = "unknown"; const std::string referenceWakeupReason3 = "100 :android,wakeup-reason-1"; const std::string referenceWakeupReason4 = "Abort: android,wakeup-reason-2"; const std::vector referenceWakeupReason5 = {"999 :android,wakeup-reason-3", "Abort: android,wakeup-reason-3"}; unique_fd wakeupReasonsWriteFd = unique_fd( TEMP_FAILURE_RETRY(open(SystemSuspendTest::wakeupReasonsFile.path, O_CLOEXEC | O_WRONLY))); MockCallbackImpl impl; sp cb = new MockCallback(&impl); bool retval = false; controlService->registerCallback(cb, &retval); ASSERT_TRUE(retval); // wakeupReason0 empty wakeup reason // Following assert check may happen before a callback been executed, iterate few checkLoop to // make sure at least one callback been finished. checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 1); ASSERT_EQ(impl.mWakeupReasons[0], referenceWakeupUnknown); // wakeupReason1 single invalid wakeup reason with only space. ASSERT_TRUE(WriteStringToFd(wakeupReason1, wakeupReasonsWriteFd)); checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 1); ASSERT_EQ(impl.mWakeupReasons[0], referenceWakeupUnknown); // wakeupReason2 two empty wakeup reasons. lseek(wakeupReasonsWriteFd, 0, SEEK_SET); ASSERT_TRUE(WriteStringToFd(wakeupReason2, wakeupReasonsWriteFd)); checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 1); ASSERT_EQ(impl.mWakeupReasons[0], referenceWakeupUnknown); // wakeupReason3 single wakeup reasons. lseek(wakeupReasonsWriteFd, 0, SEEK_SET); ASSERT_TRUE(WriteStringToFd(wakeupReason3, wakeupReasonsWriteFd)); checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 1); ASSERT_EQ(impl.mWakeupReasons[0], referenceWakeupReason3); // wakeupReason4 two wakeup reasons with one empty. lseek(wakeupReasonsWriteFd, 0, SEEK_SET); ASSERT_TRUE(WriteStringToFd(wakeupReason4, wakeupReasonsWriteFd)); checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 1); ASSERT_EQ(impl.mWakeupReasons[0], referenceWakeupReason4); // wakeupReason5 two wakeup reasons. lseek(wakeupReasonsWriteFd, 0, SEEK_SET); ASSERT_TRUE(WriteStringToFd(wakeupReason5, wakeupReasonsWriteFd)); checkLoop(3); ASSERT_EQ(impl.mWakeupReasons.size(), 2); i = 0; for (auto wakeupReason : impl.mWakeupReasons) { ASSERT_EQ(wakeupReason, referenceWakeupReason5[i++]); } cb->disable(); } // Tests that SystemSuspend HAL correctly deals with a dead callback. TEST_F(SystemSuspendTest, DeadCallback) { ASSERT_EXIT( { sp cb = new MockCallback(nullptr); bool retval = false; controlService->registerCallback(cb, &retval); ASSERT_TRUE(retval); std::exit(0); }, ::testing::ExitedWithCode(0), ""); // Dead process callback must still be dealt with either by unregistering it // or checking isOk() on every call. checkLoop(3); } // Callback that registers another callback. class CbRegisteringCb : public BnSuspendCallback { public: CbRegisteringCb(sp controlService) : mControlService(controlService) {} binder::Status notifyWakeup([[maybe_unused]] bool x, [[maybe_unused]] const std::vector& wakeupReasons) { sp cb = new MockCallback(nullptr); cb->disable(); bool retval = false; mControlService->registerCallback(cb, &retval); return binder::Status::ok(); } private: sp mControlService; }; // Tests that callback registering another callback doesn't result in a deadlock. TEST_F(SystemSuspendTest, CallbackRegisterCallbackNoDeadlock) { sp cb = new CbRegisteringCb(controlService); bool retval = false; controlService->registerCallback(cb, &retval); ASSERT_TRUE(retval); checkLoop(3); } struct MockWakelockCallbackImpl { MOCK_METHOD0(notifyAcquired, binder::Status()); MOCK_METHOD0(notifyReleased, binder::Status()); }; class MockWakelockCallback : public BnWakelockCallback { public: MockWakelockCallback(MockWakelockCallbackImpl* impl) : mImpl(impl), mDisabled(false) {} binder::Status notifyAcquired(void) { return mDisabled ? binder::Status::ok() : mImpl->notifyAcquired(); } binder::Status notifyReleased(void) { return mDisabled ? binder::Status::ok() : mImpl->notifyReleased(); } // In case we pull the rug from under MockWakelockCallback, but SystemSuspend still has an sp<> // to the object. void disable() { mDisabled = true; } private: MockWakelockCallbackImpl* mImpl; bool mDisabled; }; // Tests that nullptr can't be registered as wakelock callbacks. TEST_F(SystemSuspendTest, RegisterInvalidWakelockCallback) { bool retval = false; controlService->registerWakelockCallback(nullptr, "testLock", &retval); ASSERT_FALSE(retval); } // Tests that the a callback cannot be registeed with a wakelock twice. TEST_F(SystemSuspendTest, RegisterCallbackTwice) { bool retval = false; MockWakelockCallbackImpl impl; sp cb = new MockWakelockCallback(&impl); controlService->registerWakelockCallback(cb, "testLock", &retval); ASSERT_TRUE(retval); controlService->registerWakelockCallback(cb, "testLock", &retval); ASSERT_FALSE(retval); cb->disable(); } // Tests that the same callback can be registered with two wakelocks. TEST_F(SystemSuspendTest, RegisterSameCallbackForTwoWakelocks) { bool retval = false; MockWakelockCallbackImpl impl; sp cb = new MockWakelockCallback(&impl); controlService->registerWakelockCallback(cb, "testLock1", &retval); ASSERT_TRUE(retval); controlService->registerWakelockCallback(cb, "testLock2", &retval); ASSERT_TRUE(retval); cb->disable(); } // Tests that the two callbacks can be registered with the same wakelock. TEST_F(SystemSuspendTest, RegisterTwoCallbacksForSameWakelock) { bool retval = false; MockWakelockCallbackImpl impl; sp cb1 = new MockWakelockCallback(&impl); sp cb2 = new MockWakelockCallback(&impl); controlService->registerWakelockCallback(cb1, "testLock", &retval); ASSERT_TRUE(retval); controlService->registerWakelockCallback(cb2, "testLock", &retval); ASSERT_TRUE(retval); cb1->disable(); cb2->disable(); } // Tests that SystemSuspend HAL correctly deals with a dead wakelock callback. TEST_F(SystemSuspendTest, DeadWakelockCallback) { ASSERT_EXIT( { sp cb = new MockWakelockCallback(nullptr); bool retval = false; controlService->registerWakelockCallback(cb, "testLock", &retval); ASSERT_TRUE(retval); std::exit(0); }, ::testing::ExitedWithCode(0), ""); // Dead process callback must still be dealt with either by unregistering it // or checking isOk() on every call. std::shared_ptr testLock = acquireWakeLock("testLock"); ASSERT_TRUE(testLock->release().isOk()); } // Wakelock callback that registers another callback. class WakelockCbRegisteringCb : public BnWakelockCallback { public: WakelockCbRegisteringCb(sp controlService) : mControlService(controlService) {} binder::Status notifyAcquired(void) { sp cb = new MockWakelockCallback(nullptr); cb->disable(); bool retval = false; mControlService->registerWakelockCallback(cb, "testLock", &retval); return binder::Status::ok(); } binder::Status notifyReleased(void) { sp cb = new MockWakelockCallback(nullptr); cb->disable(); bool retval = false; mControlService->registerWakelockCallback(cb, "testLock", &retval); return binder::Status::ok(); } private: sp mControlService; }; TEST_F(SystemSuspendTest, WakelockCallbackRegisterCallbackNoDeadlock) { sp cb = new WakelockCbRegisteringCb(controlService); bool retval = false; controlService->registerWakelockCallback(cb, "testLock", &retval); ASSERT_TRUE(retval); checkWakelockLoop(3, "testLock"); } // Tests that SystemSuspend HAL correctly notifies wakelock events. TEST_F(SystemSuspendTest, CallbackNotifyWakelock) { bool retval = false; MockWakelockCallbackImpl impl1; MockWakelockCallbackImpl impl2; sp cb1 = new MockWakelockCallback(&impl1); sp cb2 = new MockWakelockCallback(&impl2); controlService->registerWakelockCallback(cb1, "testLock1", &retval); ASSERT_TRUE(retval); controlService->registerWakelockCallback(cb2, "testLock2", &retval); ASSERT_TRUE(retval); EXPECT_CALL(impl1, notifyAcquired).Times(4); EXPECT_CALL(impl1, notifyReleased).Times(4); EXPECT_CALL(impl2, notifyAcquired).Times(3); EXPECT_CALL(impl2, notifyReleased).Times(3); checkWakelockLoop(4, "testLock1"); checkWakelockLoop(3, "testLock2"); cb1->disable(); cb2->disable(); } class SystemSuspendSameThreadTest : public ::testing::Test { public: std::shared_ptr acquireWakeLock(const std::string& name = "TestLock") { std::shared_ptr wl = nullptr; auto status = suspendService->acquireWakeLock(WakeLockType::PARTIAL, name, &wl); return wl; } /** * Returns true if wake lock is found else false. */ bool findWakeLockInfoByName(const std::vector& wlStats, const std::string& name, WakeLockInfo* info) { auto it = std::find_if(wlStats.begin(), wlStats.end(), [&name](const auto& x) { return x.name == name; }); if (it != wlStats.end()) { *info = *it; return true; } return false; } bool writeStatToFile(int statDirFd, const std::string& fileName, const std::string& stat) { unique_fd statFd{TEMP_FAILURE_RETRY( openat(statDirFd, fileName.c_str(), O_CREAT | O_CLOEXEC | O_RDWR, S_IRWXU))}; if (statFd < 0) { PLOG(ERROR) << "SystemSuspend: Error opening " << fileName; return false; } if (!WriteStringToFd(stat, statFd.get())) { PLOG(ERROR) << "SystemSuspend: Error writing stat to " << fileName; return false; } return true; } bool writeStatToFile(int statDirFd, const std::string& fileName, int64_t stat) { return writeStatToFile(statDirFd, fileName, std::to_string(stat)); } /** * Creates a kernel wakelock directory and stats files. * Returns true on success else false. */ bool addKernelWakelock(const std::string& name, int64_t activeCount = 42, int64_t activeTime = 42, int64_t eventCount = 42, int64_t expireCount = 42, int64_t lastChange = 42, int64_t maxTime = 42, int64_t preventSuspendTime = 42, int64_t totalTime = 42, int64_t wakeupCount = 42) { static int id = 0; std::string kwlId = "wakeup" + std::to_string(id++); if ((mkdirat(kernelWakelockStatsFd, kwlId.c_str(), S_IRWXU)) < 0) { PLOG(ERROR) << "SystemSuspend: Error creating directory for " << kwlId; return false; } unique_fd kernelWakelockFd{TEMP_FAILURE_RETRY( openat(kernelWakelockStatsFd, kwlId.c_str(), O_DIRECTORY | O_CLOEXEC | O_RDONLY))}; if (kernelWakelockFd < 0) { PLOG(ERROR) << "SystemSuspend: Error opening " << kwlId; return false; } int fd = kernelWakelockFd.get(); return writeStatToFile(fd, "name", name) && writeStatToFile(fd, "active_count", activeCount) && writeStatToFile(fd, "active_time_ms", activeTime) && writeStatToFile(fd, "event_count", eventCount) && writeStatToFile(fd, "expire_count", expireCount) && writeStatToFile(fd, "last_change_ms", lastChange) && writeStatToFile(fd, "max_time_ms", maxTime) && writeStatToFile(fd, "prevent_suspend_time_ms", preventSuspendTime) && writeStatToFile(fd, "total_time_ms", totalTime) && writeStatToFile(fd, "wakeup_count", wakeupCount); } /** * Adds Suspend stats files to suspendStatDir. * Returns true on success else false. */ bool addSuspendStats(int64_t success = 42, int64_t fail = 42, int64_t failedFreeze = 42, int64_t failedPrepare = 42, int64_t failedSuspend = 42, int64_t failedSuspendLate = 42, int64_t failedSuspendNoirq = 42, int64_t failedResume = 42, int64_t failedResumeEarly = 42, int64_t failedResumeNoirq = 42, const std::string& lastFailedDev = "fakeDev", int64_t lastFailedErrno = 42, const std::string& lastFailedStep = "fakeStep") { int fd = suspendStatsFd.get(); return writeStatToFile(fd, "success", success) && writeStatToFile(fd, "fail", fail) && writeStatToFile(fd, "failed_freeze", failedFreeze) && writeStatToFile(fd, "failed_prepare", failedPrepare) && writeStatToFile(fd, "failed_suspend", failedSuspend) && writeStatToFile(fd, "failed_suspend_late", failedSuspendLate) && writeStatToFile(fd, "failed_suspend_noirq", failedSuspendNoirq) && writeStatToFile(fd, "failed_resume", failedResume) && writeStatToFile(fd, "failed_resume_early", failedResumeEarly) && writeStatToFile(fd, "failed_resume_noirq", failedResumeNoirq) && writeStatToFile(fd, "last_failed_dev", lastFailedDev) && writeStatToFile(fd, "last_failed_errno", lastFailedErrno) && writeStatToFile(fd, "last_failed_step", lastFailedStep); } bool removeDirectoryEntry(const std::string& path) { auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int { switch (file_type) { case FTW_D: case FTW_DP: case FTW_DNR: if (rmdir(child) == -1) { PLOG(ERROR) << "rmdir " << child; } break; case FTW_NS: default: if (rmdir(child) != -1) break; // FALLTHRU (for gcc, lint, pcc, etc; and following for clang) FALLTHROUGH_INTENDED; case FTW_F: case FTW_SL: case FTW_SLN: if (unlink(child) == -1) { PLOG(ERROR) << "unlink " << child; } break; } return 0; }; return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0; } /** * Removes all entries from directory. * Returns true on success else false. */ bool clearDirectory(const std::string& dirPath) { std::unique_ptr dp(opendir(dirPath.c_str()), &closedir); if (!dp) { return false; } rewinddir(dp.get()); struct dirent* de; while ((de = readdir(dp.get()))) { std::string name(de->d_name); if ((name == ".") || (name == "..")) { continue; } if (!removeDirectoryEntry(dirPath + "/" + name)) { PLOG(ERROR) << "SystemSuspend: Failed to remove " << name; return false; } } return true; } /** * Returns wakelock stats. */ std::vector getWakelockStats() { std::vector wlStats; controlServiceInternal->getWakeLockStats(&wlStats); return wlStats; } /** * Returns suspend stats. */ Result getSuspendStats() { return systemSuspend->getSuspendStats(); } virtual void SetUp() override { kernelWakelockStatsFd = unique_fd(TEMP_FAILURE_RETRY( open(kernelWakelockStatsDir.path, O_DIRECTORY | O_CLOEXEC | O_RDONLY))); if (kernelWakelockStatsFd < 0) { PLOG(FATAL) << "SystemSuspend: Failed to open kernel wakelock stats directory"; } suspendStatsFd = unique_fd( TEMP_FAILURE_RETRY(open(suspendStatsDir.path, O_DIRECTORY | O_CLOEXEC | O_RDONLY))); if (suspendStatsFd < 0) { PLOG(FATAL) << "SystemSuspend: Failed to open suspend_stats directory"; } // Set up same thread suspend services sp suspendControl = new SuspendControlService(); sp suspendControlInternal = new SuspendControlServiceInternal(); controlService = suspendControl; controlServiceInternal = suspendControlInternal; systemSuspend = new SystemSuspend( unique_fd(-1) /* wakeupCountFd */, unique_fd(-1) /* stateFd */, unique_fd(dup(suspendStatsFd)), 1 /* maxNativeStatsEntries */, unique_fd(dup(kernelWakelockStatsFd.get())), unique_fd(-1) /* wakeupReasonsFd */, unique_fd(-1) /* suspendTimeFd */, kSleepTimeConfig, suspendControl, suspendControlInternal); suspendService = ndk::SharedRefBase::make(systemSuspend.get()); } virtual void TearDown() override { systemSuspend->disableAutosuspend(); ASSERT_TRUE(clearDirectory(kernelWakelockStatsDir.path)); ASSERT_TRUE(clearDirectory(suspendStatsDir.path)); } sp systemSuspend; std::shared_ptr suspendService; sp controlService; sp controlServiceInternal; unique_fd kernelWakelockStatsFd; unique_fd suspendStatsFd; TemporaryDir kernelWakelockStatsDir; TemporaryDir suspendStatsDir; const SleepTimeConfig kSleepTimeConfig = { .baseSleepTime = 100ms, .maxSleepTime = 400ms, .sleepTimeScaleFactor = 1.9, .backoffThreshold = 1, .shortSuspendThreshold = 100ms, .failedSuspendBackoffEnabled = true, .shortSuspendBackoffEnabled = true, }; }; // Test that getWakeLockStats has correct information about Native WakeLocks. TEST_F(SystemSuspendSameThreadTest, GetNativeWakeLockStats) { std::string fakeWlName = "FakeLock"; { std::shared_ptr fakeLock = acquireWakeLock(fakeWlName); std::vector wlStats = getWakelockStats(); ASSERT_EQ(wlStats.size(), 1); WakeLockInfo nwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeWlName, &nwlInfo)); ASSERT_EQ(nwlInfo.name, fakeWlName); ASSERT_EQ(nwlInfo.activeCount, 1); ASSERT_EQ(nwlInfo.isActive, true); ASSERT_FALSE(nwlInfo.isKernelWakelock); ASSERT_EQ(nwlInfo.pid, getpid()); ASSERT_EQ(nwlInfo.eventCount, 0); ASSERT_EQ(nwlInfo.expireCount, 0); ASSERT_EQ(nwlInfo.preventSuspendTime, 0); ASSERT_EQ(nwlInfo.wakeupCount, 0); // We sleep so that the wake lock stats entry get updated with a different timestamp. std::this_thread::sleep_for(1s); } std::vector wlStats = getWakelockStats(); ASSERT_EQ(wlStats.size(), 1); WakeLockInfo nwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeWlName, &nwlInfo)); ASSERT_EQ(nwlInfo.name, fakeWlName); ASSERT_EQ(nwlInfo.activeCount, 1); ASSERT_GE(nwlInfo.maxTime, 1000); ASSERT_GE(nwlInfo.totalTime, 1000); ASSERT_EQ(nwlInfo.isActive, false); ASSERT_EQ(nwlInfo.activeTime, 0); // No longer active ASSERT_FALSE(nwlInfo.isKernelWakelock); ASSERT_EQ(nwlInfo.pid, getpid()); ASSERT_EQ(nwlInfo.eventCount, 0); ASSERT_EQ(nwlInfo.expireCount, 0); ASSERT_EQ(nwlInfo.preventSuspendTime, 0); ASSERT_EQ(nwlInfo.wakeupCount, 0); } // Test that getWakeLockStats has correct information about Kernel WakeLocks. TEST_F(SystemSuspendSameThreadTest, GetKernelWakeLockStats) { std::string fakeKwlName1 = "fakeKwl1"; std::string fakeKwlName2 = "fakeKwl2"; addKernelWakelock(fakeKwlName1); addKernelWakelock(fakeKwlName2, 10 /* activeCount */); std::vector wlStats = getWakelockStats(); ASSERT_EQ(wlStats.size(), 2); WakeLockInfo kwlInfo1; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeKwlName1, &kwlInfo1)); ASSERT_EQ(kwlInfo1.name, fakeKwlName1); ASSERT_EQ(kwlInfo1.activeCount, 42); ASSERT_EQ(kwlInfo1.lastChange, 42); ASSERT_EQ(kwlInfo1.maxTime, 42); ASSERT_EQ(kwlInfo1.totalTime, 42); ASSERT_EQ(kwlInfo1.isActive, true); ASSERT_EQ(kwlInfo1.activeTime, 42); ASSERT_TRUE(kwlInfo1.isKernelWakelock); ASSERT_EQ(kwlInfo1.pid, -1); ASSERT_EQ(kwlInfo1.eventCount, 42); ASSERT_EQ(kwlInfo1.expireCount, 42); ASSERT_EQ(kwlInfo1.preventSuspendTime, 42); ASSERT_EQ(kwlInfo1.wakeupCount, 42); WakeLockInfo kwlInfo2; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeKwlName2, &kwlInfo2)); ASSERT_EQ(kwlInfo2.name, fakeKwlName2); ASSERT_EQ(kwlInfo2.activeCount, 10); ASSERT_EQ(kwlInfo2.lastChange, 42); ASSERT_EQ(kwlInfo2.maxTime, 42); ASSERT_EQ(kwlInfo2.totalTime, 42); ASSERT_EQ(kwlInfo2.isActive, true); ASSERT_EQ(kwlInfo2.activeTime, 42); ASSERT_TRUE(kwlInfo2.isKernelWakelock); ASSERT_EQ(kwlInfo2.pid, -1); ASSERT_EQ(kwlInfo2.eventCount, 42); ASSERT_EQ(kwlInfo2.expireCount, 42); ASSERT_EQ(kwlInfo2.preventSuspendTime, 42); ASSERT_EQ(kwlInfo2.wakeupCount, 42); } // Test that getWakeLockStats has correct information about Native AND Kernel WakeLocks. TEST_F(SystemSuspendSameThreadTest, GetNativeAndKernelWakeLockStats) { std::string fakeNwlName = "fakeNwl"; std::string fakeKwlName = "fakeKwl"; addKernelWakelock(fakeKwlName); { std::shared_ptr fakeLock = acquireWakeLock(fakeNwlName); std::vector wlStats = getWakelockStats(); ASSERT_EQ(wlStats.size(), 2); // Native Wakelock Stats WakeLockInfo nwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeNwlName, &nwlInfo)); ASSERT_EQ(nwlInfo.name, fakeNwlName); ASSERT_EQ(nwlInfo.activeCount, 1); ASSERT_EQ(nwlInfo.isActive, true); ASSERT_FALSE(nwlInfo.isKernelWakelock); ASSERT_EQ(nwlInfo.pid, getpid()); ASSERT_EQ(nwlInfo.eventCount, 0); ASSERT_EQ(nwlInfo.expireCount, 0); ASSERT_EQ(nwlInfo.preventSuspendTime, 0); ASSERT_EQ(nwlInfo.wakeupCount, 0); // Kernel Wakelock Stats WakeLockInfo kwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeKwlName, &kwlInfo)); ASSERT_EQ(kwlInfo.name, fakeKwlName); ASSERT_EQ(kwlInfo.activeCount, 42); ASSERT_EQ(kwlInfo.lastChange, 42); ASSERT_EQ(kwlInfo.maxTime, 42); ASSERT_EQ(kwlInfo.totalTime, 42); ASSERT_EQ(kwlInfo.isActive, true); ASSERT_EQ(kwlInfo.activeTime, 42); ASSERT_TRUE(kwlInfo.isKernelWakelock); ASSERT_EQ(kwlInfo.pid, -1); ASSERT_EQ(kwlInfo.eventCount, 42); ASSERT_EQ(kwlInfo.expireCount, 42); ASSERT_EQ(kwlInfo.preventSuspendTime, 42); ASSERT_EQ(kwlInfo.wakeupCount, 42); // We sleep so that the wake lock stats entry get updated with a different timestamp. std::this_thread::sleep_for(1s); } std::vector wlStats = getWakelockStats(); ASSERT_EQ(wlStats.size(), 2); // Native Wakelock Stats WakeLockInfo nwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeNwlName, &nwlInfo)); ASSERT_EQ(nwlInfo.name, fakeNwlName); ASSERT_EQ(nwlInfo.activeCount, 1); ASSERT_GE(nwlInfo.maxTime, 1000); ASSERT_GE(nwlInfo.totalTime, 1000); ASSERT_EQ(nwlInfo.isActive, false); ASSERT_EQ(nwlInfo.activeTime, 0); // No longer active ASSERT_FALSE(nwlInfo.isKernelWakelock); ASSERT_EQ(nwlInfo.pid, getpid()); ASSERT_EQ(nwlInfo.eventCount, 0); ASSERT_EQ(nwlInfo.expireCount, 0); ASSERT_EQ(nwlInfo.preventSuspendTime, 0); ASSERT_EQ(nwlInfo.wakeupCount, 0); // Kernel Wakelock Stats (No changes expected here) WakeLockInfo kwlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeKwlName, &kwlInfo)); ASSERT_EQ(kwlInfo.name, fakeKwlName); ASSERT_EQ(kwlInfo.activeCount, 42); ASSERT_EQ(kwlInfo.lastChange, 42); ASSERT_EQ(kwlInfo.maxTime, 42); ASSERT_EQ(kwlInfo.totalTime, 42); ASSERT_EQ(kwlInfo.isActive, true); ASSERT_EQ(kwlInfo.activeTime, 42); ASSERT_TRUE(kwlInfo.isKernelWakelock); ASSERT_EQ(kwlInfo.pid, -1); ASSERT_EQ(kwlInfo.eventCount, 42); ASSERT_EQ(kwlInfo.expireCount, 42); ASSERT_EQ(kwlInfo.preventSuspendTime, 42); ASSERT_EQ(kwlInfo.wakeupCount, 42); } // Test that the least recently used native wake lock stats entry is evicted after a given // threshold. TEST_F(SystemSuspendSameThreadTest, NativeWakeLockStatsLruEviction) { std::string fakeWlName1 = "FakeLock1"; std::string fakeWlName2 = "FakeLock2"; acquireWakeLock(fakeWlName1); acquireWakeLock(fakeWlName2); std::vector wlStats = getWakelockStats(); // Max number of native stats entries was set to 1 in SystemSuspend constructor. ASSERT_EQ(wlStats.size(), 1); ASSERT_EQ(wlStats.begin()->name, fakeWlName2); WakeLockInfo wlInfo; ASSERT_TRUE(findWakeLockInfoByName(wlStats, fakeWlName2, &wlInfo)); ASSERT_FALSE(findWakeLockInfoByName(wlStats, fakeWlName1, &wlInfo)); // Evicted } // Test that GetSuspendStats has correct information. TEST_F(SystemSuspendSameThreadTest, GetSuspendStats) { addSuspendStats(); Result res = getSuspendStats(); ASSERT_RESULT_OK(res); SuspendStats stats = res.value(); ASSERT_EQ(stats.success, 42); ASSERT_EQ(stats.fail, 42); ASSERT_EQ(stats.failedFreeze, 42); ASSERT_EQ(stats.failedPrepare, 42); ASSERT_EQ(stats.failedSuspend, 42); ASSERT_EQ(stats.failedSuspendLate, 42); ASSERT_EQ(stats.failedSuspendNoirq, 42); ASSERT_EQ(stats.failedResume, 42); ASSERT_EQ(stats.failedResumeEarly, 42); ASSERT_EQ(stats.failedResumeNoirq, 42); ASSERT_EQ(stats.lastFailedDev, "fakeDev"); ASSERT_EQ(stats.lastFailedErrno, 42); ASSERT_EQ(stats.lastFailedStep, "fakeStep"); } class SuspendWakeupTest : public ::testing::Test { public: virtual void SetUp() override { Socketpair(SOCK_STREAM, &wakeupCountTestFd, &wakeupCountServiceFd); Socketpair(SOCK_STREAM, &stateTestFd, &stateServiceFd); suspendControl = new SuspendControlService(); suspendControlInternal = new SuspendControlServiceInternal(); suspendTimeFd = unique_fd(TEMP_FAILURE_RETRY(open(suspendTimeFile.path, O_CLOEXEC | O_RDONLY))); wakeupReasonsFd = unique_fd(TEMP_FAILURE_RETRY(open(wakeupReasonsFile.path, O_CLOEXEC | O_RDONLY))); systemSuspend = new SystemSuspend( std::move(wakeupCountServiceFd), std::move(stateServiceFd), unique_fd(-1) /* suspendStatsFd */, 100 /* maxStatsEntries */, unique_fd(-1) /* kernelWakelockStatsFd */, std::move(wakeupReasonsFd), std::move(suspendTimeFd), kSleepTimeConfig, suspendControl, suspendControlInternal); // Start auto-suspend. bool enabled = false; suspendControlInternal->enableAutosuspend(new BBinder(), &enabled); ASSERT_EQ(enabled, true) << "failed to start autosuspend"; } virtual void TearDown() override { systemSuspend->disableAutosuspend(); } std::shared_ptr acquireWakeLock(const std::string& name = "TestLock") { auto suspendService = ndk::SharedRefBase::make(systemSuspend.get()); std::shared_ptr wl = nullptr; auto status = suspendService->acquireWakeLock(WakeLockType::PARTIAL, name, &wl); return wl; } void wakeup(std::string wakeupReason) { ASSERT_TRUE(WriteStringToFile(wakeupReason, wakeupReasonsFile.path)); checkLoop(1); } void checkLoop(int numIter) { for (int i = 0; i < numIter; i++) { // Mock value for /sys/power/wakeup_count. std::string wakeupCount = std::to_string(rand()); ASSERT_TRUE(WriteStringToFd(wakeupCount, wakeupCountTestFd)); ASSERT_EQ(readFd(wakeupCountTestFd), wakeupCount) << "wakeup count value written by SystemSuspend is not equal to value given to it"; ASSERT_EQ(readFd(stateTestFd), "mem") << "SystemSuspend failed to write correct sleep state."; // There is a race window where sleepTime can be checked in the tests, // before it is updated in autoSuspend while (!isReadBlocked(wakeupCountTestFd)) { } } } void suspendFor(std::chrono::milliseconds suspendTime, std::chrono::milliseconds suspendOverhead, int numberOfSuspends) { std::string suspendStr = std::to_string( std::chrono::duration_cast>(suspendOverhead) .count()) + " " + std::to_string( std::chrono::duration_cast>(suspendTime).count()); ASSERT_TRUE(WriteStringToFile(suspendStr, suspendTimeFile.path)); checkLoop(numberOfSuspends); } void checkSuspendInfo(const SuspendInfo& expected) { SuspendInfo actual; systemSuspend->getSuspendInfo(&actual); ASSERT_EQ(actual.suspendAttemptCount, expected.suspendAttemptCount); ASSERT_EQ(actual.failedSuspendCount, expected.failedSuspendCount); ASSERT_EQ(actual.shortSuspendCount, expected.shortSuspendCount); ASSERT_EQ(actual.suspendTimeMillis, expected.suspendTimeMillis); ASSERT_EQ(actual.shortSuspendTimeMillis, expected.shortSuspendTimeMillis); ASSERT_EQ(actual.suspendOverheadTimeMillis, expected.suspendOverheadTimeMillis); ASSERT_EQ(actual.failedSuspendOverheadTimeMillis, expected.failedSuspendOverheadTimeMillis); ASSERT_EQ(actual.newBackoffCount, expected.newBackoffCount); ASSERT_EQ(actual.backoffContinueCount, expected.backoffContinueCount); ASSERT_EQ(actual.sleepTimeMillis, expected.sleepTimeMillis); } unique_fd wakeupCountServiceFd; unique_fd stateServiceFd; unique_fd stateTestFd; unique_fd wakeupCountTestFd; unique_fd wakeupReasonsFd; unique_fd suspendTimeFd; TemporaryFile wakeupReasonsFile; TemporaryFile suspendTimeFile; TemporaryFile stateFile; TemporaryFile wakeupCountFile; sp suspendControl; sp suspendControlInternal; sp systemSuspend; const SleepTimeConfig kSleepTimeConfig = { .baseSleepTime = 100ms, .maxSleepTime = 400ms, .sleepTimeScaleFactor = 1.9, .backoffThreshold = 1, .shortSuspendThreshold = 100ms, .failedSuspendBackoffEnabled = true, .shortSuspendBackoffEnabled = true, }; const int64_t kLongSuspendMillis = 10000; // >= kSleepTimeConfig.shortSuspendThreshold const int64_t kShortSuspendMillis = 10; // < kSleepTimeConfig.shortSuspendThreshold const int64_t kSuspendOverheadMillis = 20; }; TEST_F(SuspendWakeupTest, LongSuspendStat) { suspendFor(std::chrono::milliseconds(kLongSuspendMillis), std::chrono::milliseconds(kSuspendOverheadMillis), 1); SuspendInfo expected; expected.suspendAttemptCount = 1; expected.suspendTimeMillis = kLongSuspendMillis; expected.suspendOverheadTimeMillis = kSuspendOverheadMillis; expected.sleepTimeMillis = kSleepTimeConfig.baseSleepTime.count(); checkSuspendInfo(expected); } TEST_F(SuspendWakeupTest, ShortSuspendStat) { suspendFor(std::chrono::milliseconds(kShortSuspendMillis), std::chrono::milliseconds(kSuspendOverheadMillis), 1); SuspendInfo expected; expected.suspendAttemptCount = 1; expected.shortSuspendCount = 1; expected.shortSuspendTimeMillis = kShortSuspendMillis; expected.suspendTimeMillis = kShortSuspendMillis; expected.suspendOverheadTimeMillis = kSuspendOverheadMillis; expected.sleepTimeMillis = kSleepTimeConfig.baseSleepTime.count(); checkSuspendInfo(expected); } TEST_F(SuspendWakeupTest, ShortSuspendBackoffStat) { suspendFor(std::chrono::milliseconds(kShortSuspendMillis), std::chrono::milliseconds(kSuspendOverheadMillis), 2); SuspendInfo expected; expected.suspendAttemptCount = 2; expected.shortSuspendCount = 2; expected.shortSuspendTimeMillis = kShortSuspendMillis * 2; expected.suspendTimeMillis = kShortSuspendMillis * 2; expected.suspendOverheadTimeMillis = kSuspendOverheadMillis * 2; expected.newBackoffCount = 1; expected.sleepTimeMillis = kSleepTimeConfig.baseSleepTime.count() * 2; checkSuspendInfo(expected); } TEST_F(SuspendWakeupTest, ShortSuspendBackoffContinueStat) { suspendFor(std::chrono::milliseconds(kShortSuspendMillis), std::chrono::milliseconds(kSuspendOverheadMillis), 2); SuspendInfo expected; expected.suspendAttemptCount = 2; expected.shortSuspendCount = 2; expected.shortSuspendTimeMillis = kShortSuspendMillis * 2; expected.suspendTimeMillis = kShortSuspendMillis * 2; expected.suspendOverheadTimeMillis = kSuspendOverheadMillis * 2; expected.newBackoffCount = 1; expected.sleepTimeMillis = kSleepTimeConfig.baseSleepTime.count() * 2; checkSuspendInfo(expected); suspendFor(std::chrono::milliseconds(kShortSuspendMillis), std::chrono::milliseconds(kSuspendOverheadMillis), 1); expected.suspendAttemptCount += 1; expected.shortSuspendCount += 1; expected.shortSuspendTimeMillis += kShortSuspendMillis; expected.suspendTimeMillis += kShortSuspendMillis; expected.suspendOverheadTimeMillis += kSuspendOverheadMillis; expected.backoffContinueCount += 1; expected.sleepTimeMillis += std::chrono::round(kSleepTimeConfig.baseSleepTime * kSleepTimeConfig.sleepTimeScaleFactor) .count(); checkSuspendInfo(expected); } TEST_F(SuspendWakeupTest, GetSingleWakeupReasonStat) { wakeup("abc"); std::vector wStats; ASSERT_TRUE(suspendControlInternal->getWakeupStats(&wStats).isOk()); ASSERT_EQ(wStats.size(), 1); ASSERT_EQ(wStats[0].name, "abc"); ASSERT_EQ(wStats[0].count, 1); } TEST_F(SuspendWakeupTest, GetChainedWakeupReasonStat) { wakeup("a\nb"); std::vector wStats; ASSERT_TRUE(suspendControlInternal->getWakeupStats(&wStats).isOk()); ASSERT_EQ(wStats.size(), 1); ASSERT_EQ(wStats[0].name, "a;b"); ASSERT_EQ(wStats[0].count, 1); } TEST_F(SuspendWakeupTest, GetMultipleWakeupReasonStats) { wakeup("abc"); wakeup("d\ne"); wakeup(""); wakeup(""); wakeup("wxyz\nabc\n"); wakeup("abc"); std::vector wStats; ASSERT_TRUE(suspendControlInternal->getWakeupStats(&wStats).isOk()); ASSERT_EQ(wStats.size(), 4); ASSERT_EQ(wStats[0].name, "abc"); ASSERT_EQ(wStats[0].count, 2); ASSERT_EQ(wStats[1].name, "wxyz;abc"); ASSERT_EQ(wStats[1].count, 1); ASSERT_EQ(wStats[2].name, "unknown"); ASSERT_EQ(wStats[2].count, 2); ASSERT_EQ(wStats[3].name, "d;e"); ASSERT_EQ(wStats[3].count, 1); } TEST(WakeupListTest, TestEmpty) { WakeupList wakeupList(3); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_TRUE(wakeups.empty()); } TEST(WakeupListTest, TestNoCapacity) { WakeupList wakeupList(0); wakeupList.update({"a"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_TRUE(wakeups.empty()); } TEST(WakeupListTest, TestConcat) { WakeupList wakeupList(3); wakeupList.update({"a", "b"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_EQ(wakeups[0].name, "a;b"); ASSERT_EQ(wakeups[0].count, 1); } TEST(WakeupListTest, TestNewEntry) { WakeupList wakeupList(3); wakeupList.update({"a"}); wakeupList.update({"b"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_EQ(wakeups[1].name, "a"); ASSERT_EQ(wakeups[1].count, 1); ASSERT_EQ(wakeups[0].name, "b"); ASSERT_EQ(wakeups[0].count, 1); } TEST(WakeupListTest, TestIncrement) { WakeupList wakeupList(3); wakeupList.update({"a"}); wakeupList.update({"b"}); wakeupList.update({"a"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_EQ(wakeups[0].name, "a"); ASSERT_EQ(wakeups[0].count, 2); ASSERT_EQ(wakeups[1].name, "b"); ASSERT_EQ(wakeups[1].count, 1); } TEST(WakeupListTest, TestCapacity) { WakeupList wakeupList(3); wakeupList.update({"a"}); wakeupList.update({"b"}); wakeupList.update({"c"}); wakeupList.update({"d"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_EQ(wakeups.size(), 3); ASSERT_EQ(wakeups[0].name, "d"); ASSERT_EQ(wakeups[0].count, 1); ASSERT_EQ(wakeups[1].name, "c"); ASSERT_EQ(wakeups[1].count, 1); ASSERT_EQ(wakeups[2].name, "b"); ASSERT_EQ(wakeups[2].count, 1); } TEST(WakeupListTest, TestLRUEvict) { WakeupList wakeupList(3); wakeupList.update({"a"}); wakeupList.update({"b"}); wakeupList.update({"a"}); wakeupList.update({"c"}); wakeupList.update({"c"}); wakeupList.update({"c"}); wakeupList.update({"d"}); std::vector wakeups; wakeupList.getWakeupStats(&wakeups); ASSERT_EQ(wakeups.size(), 3); ASSERT_EQ(wakeups[0].name, "d"); ASSERT_EQ(wakeups[0].count, 1); ASSERT_EQ(wakeups[1].name, "c"); ASSERT_EQ(wakeups[1].count, 3); ASSERT_EQ(wakeups[2].name, "a"); ASSERT_EQ(wakeups[2].count, 2); } } // namespace android int main(int argc, char** argv) { android::hardware::details::setTrebleTestingOverride(true); ::testing::InitGoogleMock(&argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }