Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 091a2019 authored by Jack He's avatar Jack He
Browse files

GD: Implement Wakelock Manager and Native Wakelock

* Implement Wakelock Manager to support both OS callout-based wakelocks
  and Android native wakelocks
* Use ISystemSuspend for Android native wakelocks
* Allow runtime override of wakelock callout to bypass the native
  implementation
* Implement unit test for both native and OS callout wakelocks

Bug: 184608842
Bug: 188614066
Tag: #gd-refactor
Test: atest bluetooth_test_gd_unit
BYPASS_LONG_LINES_REASON: Bluetooth likes 120 lines

Change-Id: I428e06e6a48a423bb6912e3449895e030e0dd073
parent df847fb4
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ cc_defaults {
                "android.hardware.bluetooth@1.0",
                "android.hardware.bluetooth@1.1",
                "android.system.suspend.control-V1-ndk",
                "android.system.suspend@1.0",
                "libbinder_ndk",
                "libcutils",
                "libhidlbase",
@@ -260,6 +261,7 @@ cc_binary {
                "android.hardware.bluetooth@1.0",
                "android.hardware.bluetooth@1.1",
                "android.system.suspend.control-V1-ndk",
                "android.system.suspend@1.0",
                "libbinder_ndk",
                "libhidlbase",
                "libutils",
@@ -311,6 +313,7 @@ cc_test {
                "android.hardware.bluetooth@1.0",
                "android.hardware.bluetooth@1.1",
                "android.system.suspend.control-V1-ndk",
                "android.system.suspend@1.0",
                "libbinder_ndk",
                "libhidlbase",
                "libutils",
@@ -392,6 +395,7 @@ cc_test {
                "android.hardware.bluetooth@1.0",
                "android.hardware.bluetooth@1.1",
                "android.system.suspend.control-V1-ndk",
                "android.system.suspend@1.0",
                "libbinder_ndk",
                "libhidlbase",
                "libutils",
@@ -661,6 +665,7 @@ genrule {
        "hci/hci_acl_manager.fbs",
        "l2cap/classic/l2cap_classic_module.fbs",
        "shim/dumpsys.fbs",
        "os/wakelock_manager.fbs",
    ],
    out: [
        "activity_attribution.bfbs",
@@ -669,6 +674,7 @@ genrule {
        "dumpsys_data.bfbs",
        "hci_acl_manager.bfbs",
        "l2cap_classic_module.bfbs",
        "wakelock_manager.bfbs",
    ],
}

@@ -685,6 +691,7 @@ genrule {
        "hci/hci_acl_manager.fbs",
        "l2cap/classic/l2cap_classic_module.fbs",
        "shim/dumpsys.fbs",
        "os/wakelock_manager.fbs",
    ],
    out: [
        "activity_attribution_generated.h",
@@ -693,6 +700,7 @@ genrule {
        "hci_acl_manager_generated.h",
        "init_flags_generated.h",
        "l2cap_classic_module_generated.h",
        "wakelock_manager_generated.h",
    ],
}

+5 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ filegroup {
        "android/metrics.cc",
        "android/parameter_provider.cc",
        "android/system_properties.cc",
        "android/wakelock_native.cc",
    ],
}

@@ -20,6 +21,7 @@ filegroup {
    name: "BluetoothOsTestSources_android",
    srcs: [
        "android/system_properties_test.cc",
        "android/wakelock_native_test.cc",
    ],
}

@@ -29,6 +31,7 @@ filegroup {
        "host/metrics.cc",
        "host/parameter_provider.cc",
        "host/system_properties.cc",
        "host/wakelock_native.cc",
    ],
}

@@ -50,6 +53,7 @@ filegroup {
        "linux_generic/repeating_alarm.cc",
        "linux_generic/reactive_semaphore.cc",
        "linux_generic/thread.cc",
        "linux_generic/wakelock_manager.cc",
    ],
}

@@ -63,6 +67,7 @@ filegroup {
        "linux_generic/reactor_unittest.cc",
        "linux_generic/repeating_alarm_unittest.cc",
        "linux_generic/thread_unittest.cc",
        "linux_generic/wakelock_manager_unittest.cc",
    ],
}

+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ source_set("BluetoothOsSources_linux") {
    "linux/metrics.cc",
    "linux/parameter_provider.cc",
    "linux/system_properties.cc",
    "linux/wakelock_native.cc",
  ]

  configs += [ "//bt/gd:gd_defaults" ]
@@ -32,6 +33,7 @@ source_set("BluetoothOsSources_linux_generic") {
    "linux_generic/reactor.cc",
    "linux_generic/repeating_alarm.cc",
    "linux_generic/thread.cc",
    "linux_generic/wakelock_manager.cc",
  ]

  configs += [ "//bt/gd:gd_defaults" ]
+118 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2021 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.
 *
 ******************************************************************************/

#define LOG_TAG "BtGdWakelockNative"

#include "os/internal/wakelock_native.h"

#include <android/system/suspend/1.0/ISystemSuspend.h>
#include <fcntl.h>
#include <unistd.h>

#include <cerrno>
#include <string>

#include "os/log.h"

namespace bluetooth {
namespace os {
namespace internal {

using android::sp;
using android::system::suspend::V1_0::ISystemSuspend;
using android::system::suspend::V1_0::IWakeLock;
using android::system::suspend::V1_0::WakeLockType;

struct WakelockNative::Impl {
  sp<ISystemSuspend> suspend_service = nullptr;
  sp<IWakeLock> current_wakelock = nullptr;

  class SystemSuspendDeathRecipient : public ::android::hardware::hidl_death_recipient {
   public:
    explicit SystemSuspendDeathRecipient(WakelockNative::Impl* impl) : impl_(impl) {}
    void serviceDied(uint64_t /*cookie*/, const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
      LOG_ERROR("ISystemSuspend HAL service died!");
      impl_->suspend_service = nullptr;
    }

   private:
    WakelockNative::Impl* impl_ = nullptr;
  };
  sp<SystemSuspendDeathRecipient> suspend_death_recipient;
};

void WakelockNative::Initialize() {
  LOG_INFO("Initializing native wake locks");
  pimpl_->suspend_service = ISystemSuspend::getService();
  ASSERT_LOG(pimpl_->suspend_service, "Cannot get ISystemSuspend service");
  pimpl_->suspend_death_recipient = new Impl::SystemSuspendDeathRecipient(pimpl_.get());
  pimpl_->suspend_service->linkToDeath(pimpl_->suspend_death_recipient, 0 /* cookie */);
}

WakelockNative::StatusCode WakelockNative::Acquire(const std::string& lock_name) {
  if (!pimpl_->suspend_service) {
    LOG_ERROR("lock not acquired, ISystemService is not available");
    return StatusCode::NATIVE_SERVICE_NOT_AVAILABLE;
  }

  if (pimpl_->current_wakelock) {
    LOG_INFO("wakelock is already acquired");
    return StatusCode::SUCCESS;
  }

  pimpl_->current_wakelock = pimpl_->suspend_service->acquireWakeLock(WakeLockType::PARTIAL, lock_name);
  if (!pimpl_->current_wakelock) {
    LOG_ERROR("wake lock not acquired: %s", strerror(errno));
    return StatusCode::NATIVE_API_ERROR;
  }

  return StatusCode::SUCCESS;
}

WakelockNative::StatusCode WakelockNative::Release(const std::string& lock_name) {
  if (!pimpl_->current_wakelock) {
    LOG_WARN("no lock is currently acquired");
    return StatusCode::SUCCESS;
  }
  pimpl_->current_wakelock->release();
  pimpl_->current_wakelock.clear();
  return StatusCode::SUCCESS;
}

void WakelockNative::CleanUp() {
  LOG_INFO("Cleaning up native wake locks");
  if (pimpl_->current_wakelock) {
    LOG_INFO("releasing current wakelock during clean up");
    pimpl_->current_wakelock->release();
    pimpl_->current_wakelock.clear();
  }
  if (pimpl_->suspend_service) {
    LOG_INFO("Unlink death recipient");
    pimpl_->suspend_service->unlinkToDeath(pimpl_->suspend_death_recipient);
    pimpl_->suspend_death_recipient.clear();
    pimpl_->suspend_service.clear();
  }
}

WakelockNative::WakelockNative() : pimpl_(std::make_unique<Impl>()) {}

WakelockNative::~WakelockNative() = default;

}  // namespace internal
}  // namespace os
}  // namespace bluetooth
 No newline at end of file
+273 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  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 <aidl/android/system/suspend/BnSuspendCallback.h>
#include <aidl/android/system/suspend/BnWakelockCallback.h>
#include <aidl/android/system/suspend/ISuspendControlService.h>
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>

#include <chrono>
#include <future>
#include <memory>
#include <mutex>

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<std::promise<void>> acquire_promise = nullptr;
static std::unique_ptr<std::promise<void>> release_promise = nullptr;

class PromiseFutureContext {
 public:
  static void FulfilPromise(std::unique_ptr<std::promise<void>>& promise) {
    std::lock_guard<std::recursive_mutex> lock_guard(mutex);
    if (promise != nullptr) {
      promise->set_value();
      promise = nullptr;
    }
  }

  explicit PromiseFutureContext(std::unique_ptr<std::promise<void>>& promise, bool expect_fulfillment)
      : promise_(promise), expect_fulfillment_(expect_fulfillment) {
    std::lock_guard<std::recursive_mutex> lock_guard(mutex);
    EXPECT_EQ(promise_, nullptr);
    promise_ = std::make_unique<std::promise<void>>();
    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<std::recursive_mutex> lock_guard(mutex);
    promise_ = nullptr;
  }

 private:
  std::unique_ptr<std::promise<void>>& promise_;
  bool expect_fulfillment_ = true;
  std::future<void> future_;
};

class WakelockCallback : public BnWakelockCallback {
 public:
  ScopedAStatus notifyAcquired() override {
    std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::string>& wakeup_reasons) override {
    std::lock_guard<std::recursive_mutex> 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<SuspendCallback> suspend_callback = nullptr;
static std::shared_ptr<WakelockCallback> 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<SuspendCallback>();
      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<WakelockCallback>();
      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<ISuspendControlService> 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
 No newline at end of file
Loading