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

Commit 31a33e4d authored by Chris Manton's avatar Chris Manton
Browse files

Add timer capability for shim stack

The le advertising and scanning features
require timers to stop functionality after the
requested interval.

Bug: 146367779
Test: bluetooth_legacy_test
Change-Id: If7223eaff30785c661bca7f90d94462772d576bc
parent c413fec5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ filegroup {
        "l2c_api.cc",
        "l2cap.cc",
        "shim.cc",
        "timer.cc",
    ]
}

@@ -16,6 +17,7 @@ filegroup {
    name: "LibBluetoothShimTestSources",
    srcs: [
        "l2cap_test.cc",
        "timer_test.cc",
    ]
}
+5 −5
Original line number Diff line number Diff line
@@ -22,11 +22,11 @@
#include "main/shim/stub/osi.h"
#include "osi/include/alarm.h"

bool osi_property_get_int32(char const* n, int a) { return true; }

const module_t* get_module(const char*) { return nullptr; };
bool module_init(module_t const*) { return true; };
void module_clean_up(module_t const*){};
bool module_init(module_t const*) { return true; }
bool module_start_up(module_t const*) { return true; }
const module_t* get_module(const char*) { return nullptr; }
void module_clean_up(module_t const*) {}
void module_shut_down(module_t const*) {}

void* osi_alloc(size_t size) { return malloc(size); }
void* osi_calloc(size_t size) { return calloc(1, size); }
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.
 */

#define LOG_TAG "bt_shim_timer"

#include <base/bind.h>
#include <cstdint>
#include <functional>

#include "main/shim/shim.h"
#include "main/shim/timer.h"
#include "osi/include/alarm.h"
#include "osi/include/log.h"

static void timer_timeout(void* data) {
  CHECK(data != nullptr);
  bluetooth::shim::Timer* timeout = static_cast<bluetooth::shim::Timer*>(data);
  bluetooth::shim::Post(
      base::Bind(&bluetooth::shim::Timer::Pop, base::Unretained(timeout)));
}

void bluetooth::shim::Timer::Set(uint64_t duration_ms,
                                 std::function<void()> func) {
  CHECK(duration_ms != 0);
  callback_ = func;
  alarm_set_on_mloop(timer_, duration_ms, timer_timeout, (void*)this);
}

void bluetooth::shim::Timer::Cancel() {
  alarm_cancel(timer_);
  callback_ = {};
}

bool bluetooth::shim::Timer::IsActive() { return callback_ != nullptr; }

bluetooth::shim::Timer::Timer(const char* name) {
  timer_ = alarm_new(name);
  CHECK(timer_ != nullptr);
}

bluetooth::shim::Timer::~Timer() { alarm_free(timer_); }

void bluetooth::shim::Timer::Pop(Timer* timer) {
  timer->callback_();
  timer->callback_ = {};
}
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.
 */

#pragma once

#include <cstdint>
#include <functional>
#include "osi/include/alarm.h"

namespace bluetooth {
namespace shim {

class Timer {
 public:
  /**
   * Set this timer using the osi timer alarm functionality.
   *
   * The alarm duration *must not* be zero.  The timer is set
   * on the bluetooth main message loop thread.
   *
   * @param duration_ms Duration in milliseconds (>0) before alarm pops
   * @param func Function to execute upon alarm pop.
   */
  void Set(uint64_t duration_ms, std::function<void()> func);

  /**
   * Cancel this previously set timer.
   *
   * The associated function call will *not* be executed.
   */
  void Cancel();

  /**
   * Determine if a given timer has been set or not.
   *
   * @return |true| if timer has been set, |false| otherwise.
   */
  bool IsActive();

  /**
   * @param name Arbitrary name passed to the osi module.
   */
  Timer(const char* name);
  ~Timer();

  /**
   * Pop this timer.
   *
   * Called from an internal trampoline timeout global function registered
   * with osi alarm.  This trampoline function will then post
   * the execution of the callback function onto the shim thread.
   */
  static void Pop(Timer* timer);

 private:
  std::function<void()> callback_{};
  alarm_t* timer_{nullptr};
};

}  // namespace shim
}  // namespace bluetooth
+145 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 <gtest/gtest.h>
#include <cstdint>
#include <future>

#define LOG_TAG "bt_shim_test"

#include <base/logging.h>
#include "osi/include/log.h"
#include "shim/timer.h"
#include "stub/osi.h"

#include <stdlib.h>

namespace bluetooth {
namespace legacy {

constexpr uint64_t kDurationMs = 3;

static const char* kTimer0 = "TestTimer00";
static const char* kTimer1 = "TestTimer01";

namespace {

class TimerTest : public testing::Test {
 public:
  void SetUp() override {
    // Ensure expected global state default initial conditions
    bluetooth::shim::stub::name_to_alarm_map_.clear();
  }

  void TearDown() override {
    // Reset global state to defaults
    bluetooth::shim::stub::name_to_alarm_map_.clear();
  }
};

TEST_F(TimerTest, Set) {
  std::promise<void> promise;
  auto future = promise.get_future();

  shim::Timer* timer = new shim::Timer(kTimer0);

  timer->Set(kDurationMs, [&promise]() { promise.set_value(); });

  CHECK(bluetooth::shim::stub::alarm_is_set(kTimer0) == true);
  CHECK(bluetooth::shim::stub::alarm_interval_ms(kTimer0) == kDurationMs);
  CHECK(bluetooth::shim::stub::alarm_data(kTimer0));

  {
    shim::Timer* timer =
        static_cast<shim::Timer*>(bluetooth::shim::stub::alarm_data(kTimer0));
    bluetooth::shim::Timer::Pop(timer);
  }
  future.wait();

  delete timer;
  CHECK(bluetooth::shim::stub::name_to_alarm_map_.empty());
}

TEST_F(TimerTest, Set2) {
  std::promise<void> promise0;
  std::promise<void> promise1;
  auto future0 = promise0.get_future();
  auto future1 = promise1.get_future();

  shim::Timer* timer0 = new shim::Timer(kTimer0);
  CHECK(bluetooth::shim::stub::name_to_alarm_map_.size() == 1);

  shim::Timer* timer1 = new shim::Timer(kTimer1);
  CHECK(bluetooth::shim::stub::name_to_alarm_map_.size() == 2);

  timer0->Set(kDurationMs, [&promise0]() { promise0.set_value(); });

  timer1->Set(kDurationMs * 2, [&promise1]() { promise1.set_value(); });

  CHECK(bluetooth::shim::stub::alarm_is_set(kTimer0) == true);
  CHECK(bluetooth::shim::stub::alarm_interval_ms(kTimer0) == kDurationMs);
  CHECK(bluetooth::shim::stub::alarm_data(kTimer0));

  CHECK(bluetooth::shim::stub::alarm_is_set(kTimer1) == true);
  CHECK(bluetooth::shim::stub::alarm_interval_ms(kTimer1) == kDurationMs * 2);
  CHECK(bluetooth::shim::stub::alarm_data(kTimer1));

  {
    shim::Timer* timer =
        static_cast<shim::Timer*>(bluetooth::shim::stub::alarm_data(kTimer0));
    bluetooth::shim::Timer::Pop(timer);
  }

  {
    shim::Timer* timer =
        static_cast<shim::Timer*>(bluetooth::shim::stub::alarm_data(kTimer1));
    bluetooth::shim::Timer::Pop(timer);
  }

  future0.wait();
  future1.wait();

  delete timer0;
  delete timer1;

  CHECK(bluetooth::shim::stub::name_to_alarm_map_.empty());
}

TEST_F(TimerTest, Cancel) {
  std::promise<void> promise;
  auto future = promise.get_future();

  shim::Timer* timer = new shim::Timer(kTimer0);

  timer->Set(kDurationMs, [&promise]() { promise.set_value(); });

  CHECK(bluetooth::shim::stub::alarm_is_set(kTimer0) == true);
  CHECK(bluetooth::shim::stub::alarm_interval_ms(kTimer0) == kDurationMs);
  CHECK(bluetooth::shim::stub::alarm_data(kTimer0));

  timer->Cancel();

  CHECK(bluetooth::shim::stub::alarm_is_set(kTimer0) == false);
  CHECK(bluetooth::shim::stub::alarm_interval_ms(kTimer0) == 0);
  CHECK(bluetooth::shim::stub::alarm_data(kTimer0) == nullptr);

  delete timer;
  CHECK(bluetooth::shim::stub::name_to_alarm_map_.empty());
}

}  // namespace
}  // namespace legacy
}  // namespace bluetooth