/****************************************************************************** * * Copyright 2020 Google, Inc. * * 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 "os/internal/wakelock_native.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace testing { using aidl::android::system::suspend::BnSuspendCallback; using aidl::android::system::suspend::BnWakelockCallback; using aidl::android::system::suspend::ISuspendControlService; using bluetooth::os::internal::WakelockNative; using ndk::ScopedAStatus; using ndk::SharedRefBase; using ndk::SpAIBinder; static const std::string kTestWakelockName = "BtWakelockNativeTestLock"; static std::recursive_mutex mutex; static std::unique_ptr> acquire_promise = nullptr; static std::unique_ptr> release_promise = nullptr; class PromiseFutureContext { public: static void FulfilPromise(std::unique_ptr>& promise) { std::lock_guard lock_guard(mutex); if (promise != nullptr) { promise->set_value(); promise = nullptr; } } explicit PromiseFutureContext(std::unique_ptr>& promise, bool expect_fulfillment) : promise_(promise), expect_fulfillment_(expect_fulfillment) { std::lock_guard lock_guard(mutex); EXPECT_EQ(promise_, nullptr); promise_ = std::make_unique>(); future_ = promise->get_future(); } ~PromiseFutureContext() { auto future_status = future_.wait_for(std::chrono::seconds(2)); if (expect_fulfillment_) { EXPECT_EQ(future_status, std::future_status::ready); } else { EXPECT_NE(future_status, std::future_status::ready); } std::lock_guard lock_guard(mutex); promise_ = nullptr; } private: std::unique_ptr>& promise_; bool expect_fulfillment_ = true; std::future future_; }; class WakelockCallback : public BnWakelockCallback { public: ScopedAStatus notifyAcquired() override { std::lock_guard lock_guard(mutex); net_acquired_count++; fprintf(stderr, "notifyAcquired, count = %d\n", net_acquired_count); PromiseFutureContext::FulfilPromise(acquire_promise); return ScopedAStatus::ok(); } ScopedAStatus notifyReleased() override { std::lock_guard lock_guard(mutex); net_acquired_count--; fprintf(stderr, "notifyReleased, count = %d\n", net_acquired_count); PromiseFutureContext::FulfilPromise(release_promise); return ScopedAStatus::ok(); } int net_acquired_count = 0; }; class SuspendCallback : public BnSuspendCallback { public: ScopedAStatus notifyWakeup(bool success, const std::vector& wakeup_reasons) override { std::lock_guard lock_guard(mutex); fprintf(stderr, "notifyWakeup\n"); return ScopedAStatus::ok(); } }; // There is no way to unregister these callbacks besides when this process dies // Hence, we want to have only one copy of these callbacks per process static std::shared_ptr suspend_callback = nullptr; static std::shared_ptr control_callback = nullptr; class WakelockNativeTest : public Test { protected: void SetUp() override { ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); WakelockNative::Get().Initialize(); auto binder_raw = AServiceManager_getService("suspend_control"); ASSERT_NE(binder_raw, nullptr); binder.set(binder_raw); control_service_ = ISuspendControlService::fromBinder(binder); if (control_service_ == nullptr) { FAIL() << "Fail to obtain suspend_control"; } if (suspend_callback == nullptr) { suspend_callback = SharedRefBase::make(); bool is_registered = false; ScopedAStatus status = control_service_->registerCallback(suspend_callback, &is_registered); if (!is_registered || !status.isOk()) { FAIL() << "Fail to register suspend callback"; } } if (control_callback == nullptr) { control_callback = SharedRefBase::make(); bool is_registered = false; ScopedAStatus status = control_service_->registerWakelockCallback(control_callback, kTestWakelockName, &is_registered); if (!is_registered || !status.isOk()) { FAIL() << "Fail to register wakeup callback"; } } control_callback->net_acquired_count = 0; } void TearDown() override { control_service_ = nullptr; binder.set(nullptr); WakelockNative::Get().CleanUp(); } SpAIBinder binder; std::shared_ptr control_service_ = nullptr; }; TEST_F(WakelockNativeTest, test_acquire_and_release_wakelocks) { ASSERT_EQ(control_callback->net_acquired_count, 0); { PromiseFutureContext context(acquire_promise, true); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(release_promise, true); auto status = WakelockNative::Get().Release(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 0); } TEST_F(WakelockNativeTest, test_acquire_and_release_wakelocks_repeated_acquire) { ASSERT_EQ(control_callback->net_acquired_count, 0); { PromiseFutureContext context(acquire_promise, true); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(acquire_promise, false); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(release_promise, true); auto status = WakelockNative::Get().Release(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 0); } TEST_F(WakelockNativeTest, test_acquire_and_release_wakelocks_repeated_release) { ASSERT_EQ(control_callback->net_acquired_count, 0); { PromiseFutureContext context(acquire_promise, true); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(release_promise, true); auto status = WakelockNative::Get().Release(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 0); { PromiseFutureContext context(release_promise, false); auto status = WakelockNative::Get().Release(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 0); } TEST_F(WakelockNativeTest, test_acquire_and_release_wakelocks_in_a_loop) { ASSERT_EQ(control_callback->net_acquired_count, 0); for (int i = 0; i < 10; ++i) { { PromiseFutureContext context(acquire_promise, true); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(release_promise, true); auto status = WakelockNative::Get().Release(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 0); } } TEST_F(WakelockNativeTest, test_clean_up) { WakelockNative::Get().Initialize(); ASSERT_EQ(control_callback->net_acquired_count, 0); { PromiseFutureContext context(acquire_promise, true); auto status = WakelockNative::Get().Acquire(kTestWakelockName); ASSERT_EQ(status, WakelockNative::StatusCode::SUCCESS); } ASSERT_EQ(control_callback->net_acquired_count, 1); { PromiseFutureContext context(release_promise, true); WakelockNative::Get().CleanUp(); } ASSERT_EQ(control_callback->net_acquired_count, 0); } } // namespace testing