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

Commit 4d3f1ede authored by Hansong Zhang's avatar Hansong Zhang
Browse files

Observer Registry

Helper for client (observer) to track registration and drop callbacks if
observer is unregistered

Test: bluetooth_test_gd
Change-Id: Icc58d812bccbcb85c8b8142c659503636f498b6a
parent 5dd87969
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,5 +13,6 @@ filegroup {
        "blocking_queue_unittest.cc",
        "class_of_device_unittest.cc",
        "bidi_queue_unittest.cc",
        "observer_registry_test.cc",
    ],
}
+75 −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 <array>

#include "common/bind.h"
#include "common/callback.h"
#include "os/log.h"

namespace bluetooth {
namespace common {

// Tracks an observer registration on client (observer) code. Register() returns a wrapped callback object which can
// be passed to server's register API. Unregister() invalidates the wrapped callback so all callbacks that are posted
// to the client handler after the client called Unregister() call and before the server processed the Unregister()
// call on its handler, are dropped.
// Note: Register() invalidates the previous registration.
class SingleObserverRegistry {
 public:
  template <typename R, typename... T>
  decltype(auto) Register(Callback<R(T...)> callback) {
    session_++;
    return Bind(&SingleObserverRegistry::callback_wrapper<R, T...>, Unretained(this), session_, callback);
  }

  void Unregister() {
    session_++;
  }

 private:
  template <typename R, typename... T>
  void callback_wrapper(int session, Callback<R(T...)> callback, T... t) {
    if (session == session_) {
      callback.Run(std::forward<T>(t)...);
    }
  }

  uint8_t session_ = 0;
};

// Tracks observer registration for multiple event type. Each event type is represented as an integer in [0, Capacity).
template <int Capacity = 10>
class MultipleObserverRegistry {
 public:
  template <typename R, typename... T>
  decltype(auto) Register(int event_type, Callback<R(T...)> callback) {
    ASSERT(event_type < Capacity);
    return registry_[event_type].Register(callback);
  }

  void Unregister(int event_type) {
    ASSERT(event_type < Capacity);
    registry_[event_type].Unregister();
  }

  std::array<SingleObserverRegistry, Capacity> registry_;
};

}  // namespace common
}  // namespace bluetooth
+120 −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 "common/observer_registry.h"

#include "common/bind.h"
#include "gtest/gtest.h"

namespace bluetooth {
namespace common {

class SingleObserverRegistryTest : public ::testing::Test {
 protected:
  void SetUp() override {
    registry_ = new SingleObserverRegistry;
  }

  void TearDown() override {
    delete registry_;
  }

  SingleObserverRegistry* registry_;
};

void Increment(int* count) {
  (*count)++;
}

void IncrementBy(int* count, int n) {
  (*count) += n;
}

TEST_F(SingleObserverRegistryTest, wrapped_callback) {
  int count = 0;
  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
  wrapped_callback.Run();
  EXPECT_EQ(count, 1);
  wrapped_callback.Run();
  EXPECT_EQ(count, 2);
  wrapped_callback.Run();
  EXPECT_EQ(count, 3);
  registry_->Unregister();
}

TEST_F(SingleObserverRegistryTest, unregister) {
  int count = 0;
  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
  registry_->Unregister();
  wrapped_callback.Run();
  EXPECT_EQ(count, 0);
}

TEST_F(SingleObserverRegistryTest, second_register) {
  int count = 0;
  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
  registry_->Unregister();
  auto wrapped_callback2 = registry_->Register(Bind(&Increment, Unretained(&count)));
  wrapped_callback.Run();
  EXPECT_EQ(count, 0);
  wrapped_callback2.Run();
  EXPECT_EQ(count, 1);
}

class MultipleObserverRegistryTest : public ::testing::Test {
 protected:
  void SetUp() override {
    registry_ = new MultipleObserverRegistry<2>;
  }

  void TearDown() override {
    delete registry_;
  }

  MultipleObserverRegistry<2>* registry_;
};

TEST_F(MultipleObserverRegistryTest, single_wrapped_callback) {
  int count = 0;
  auto wrapped_callback = registry_->Register(0, Bind(&Increment, Unretained(&count)));
  wrapped_callback.Run();
  EXPECT_EQ(count, 1);
  wrapped_callback.Run();
  EXPECT_EQ(count, 2);
  wrapped_callback.Run();
  EXPECT_EQ(count, 3);
  registry_->Unregister(0);
}

TEST_F(MultipleObserverRegistryTest, multiple_wrapped_callback) {
  int count = 0;
  auto wrapped_callback0 = registry_->Register(0, Bind(&Increment, Unretained(&count)));
  auto wrapped_callback1 = registry_->Register(1, Bind(&IncrementBy, Unretained(&count), 10));
  wrapped_callback0.Run();
  EXPECT_EQ(count, 1);
  wrapped_callback1.Run();
  EXPECT_EQ(count, 11);
  registry_->Unregister(0);
  wrapped_callback0.Run();
  EXPECT_EQ(count, 11);
  wrapped_callback1.Run();
  EXPECT_EQ(count, 21);
  registry_->Unregister(1);
  EXPECT_EQ(count, 21);
}

}  // namespace common
}  // namespace bluetooth