1 /*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <bluetooth/log.h>
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <unistd.h>
21
22 #include <cstdlib>
23 #include <memory>
24
25 #include "common/circular_buffer.h"
26 #include "common/strings.h"
27 #include "gd/module_jniloop.h"
28 #include "gd/module_mainloop.h"
29 #include "hci/include/packet_fragmenter.h"
30 #include "main/shim/dumpsys.h"
31 #include "main/shim/entry.h"
32 #include "main/shim/stack.h"
33 #include "module.h"
34 #include "os/thread.h"
35 #include "shim/dumpsys.h"
36 #include "stack/btm/btm_int_types.h"
37 #include "stack/btm/btm_sec_cb.h"
38 #include "stack/include/main_thread.h"
39 #include "test/mock/mock_main_shim_entry.h"
40
41 using ::testing::_;
42
43 using namespace bluetooth;
44 using namespace testing;
45
46 tBTM_CB btm_cb{}; // main::shim::le_scanning_manager
47 tBTM_SEC_CB btm_sec_cb{}; // main::shim::acl
48
packet_fragmenter_get_interface()49 const packet_fragmenter_t* packet_fragmenter_get_interface() {
50 return nullptr;
51 } // main::shim::hci_layer
52 bluetooth::common::TimestamperInMilliseconds
53 timestamper_in_milliseconds; // main::shim::le_scanning_manager
54
55 namespace {
56 constexpr char kLogTagStopped[] = "STOPPED";
57 constexpr char kLogTagStarting[] = "STARTING";
58 constexpr char kLogTagStarted[] = "STARTED";
59 constexpr char kLogTagQuiescing[] = "QUIESCING";
60 constexpr char kLogTagQuiesced[] = "QUIESCED";
61
62 constexpr char kTestStackThreadName[] = "test_stack_thread";
63 constexpr int kSyncMainLoopTimeoutMs = 3000;
64 constexpr int kWaitUntilHandlerStoppedMs = 2000;
65 constexpr size_t kNumTestClients = 10;
66
67 constexpr size_t kTagLength = 48 + sizeof(' ') + sizeof(' ');
log_tag(std::string tag)68 inline void log_tag(std::string tag) {
69 std::string prepend(kTagLength / 2 - tag.size() / 2, '=');
70 std::string append(kTagLength / 2 - tag.size() / 2, '=');
71 log::info("{} {} {}", prepend, tag, append);
72 }
73
74 class MainThread {
75 public:
MainThread()76 MainThread() {
77 main_thread_start_up();
78 post_on_bt_main([]() { log::info("<=== tid Main loop started"); });
79 }
80
~MainThread()81 ~MainThread() {
82 sync_main_handler();
83 main_thread_shut_down();
84 }
85
86 private:
sync_main_handler()87 void sync_main_handler() {
88 std::promise promise = std::promise<void>();
89 std::future future = promise.get_future();
90 post_on_bt_main([&promise]() { promise.set_value(); });
91 future.wait_for(std::chrono::milliseconds(kSyncMainLoopTimeoutMs));
92 }
93 };
94
95 class TestStackManager {
96 public:
TestStackManager()97 TestStackManager() {
98 // Stack manager is started in the test after each test uses the default
99 // or adds their own modules
100 }
101
~TestStackManager()102 ~TestStackManager() {
103 log::debug("Deleting stack manager");
104 Stop();
105 }
106
107 TestStackManager(const TestStackManager&) = delete;
108
109 template <typename T>
AddModule()110 void AddModule() {
111 modules_.add<T>();
112 }
113
Start()114 void Start() {
115 if (stack_started_) return;
116 log::info("Starting up stack manager");
117 stack_started_ = true;
118 bluetooth::os::Thread* stack_thread = new bluetooth::os::Thread(
119 kTestStackThreadName, bluetooth::os::Thread::Priority::NORMAL);
120 bluetooth::shim::Stack::GetInstance()->StartModuleStack(&modules_,
121 stack_thread);
122 bluetooth::shim::Stack::GetInstance()->GetHandler()->Call(
123 []() { log::info("<=== tid GD Event loop started"); });
124 }
125
Stop()126 void Stop() {
127 if (!stack_started_) return;
128 stack_started_ = false;
129 bluetooth::shim::Stack::GetInstance()->Stop();
130 }
131
132 // NOTE: Stack manager *must* be active else method returns nullptr
133 // if stack manager has not started or shutdown
134 template <typename T>
GetUnsafeModule()135 static T* GetUnsafeModule() {
136 return bluetooth::shim::Stack::GetInstance()
137 ->GetStackManager()
138 ->GetInstance<T>();
139 }
140
NumModules() const141 size_t NumModules() const { return modules_.NumModules(); }
142
143 private:
144 bluetooth::ModuleList modules_;
145 bool stack_started_{false};
146 };
147
148 // Data returned via callback from a stack managed module
149 struct TestCallbackData {
150 int iter;
151 std::string tag;
152 };
153
154 // Data sent to a stack managed module via a module API
155 struct TestData {
156 int iter;
157 std::string tag;
158 std::function<void(TestCallbackData callback_data)> callback;
159 };
160
161 } // namespace
162
163 class TestStackDumpsysBase : public bluetooth::Module,
164 public ModuleMainloop,
165 public ModuleJniloop {
166 public:
167 TestStackDumpsysBase(const TestStackDumpsysBase&) = delete;
168 TestStackDumpsysBase& operator=(const TestStackDumpsysBase&) = delete;
169
~TestStackDumpsysBase()170 virtual ~TestStackDumpsysBase(){};
171 static const ModuleFactory Factory;
172
TestMethod(TestData test_data) const173 virtual void TestMethod(TestData test_data) const {
174 log::info("Test base class iter:{} tag:{}", test_data.iter, test_data.tag);
175 }
176
177 protected:
ListDependencies(ModuleList *) const178 void ListDependencies(ModuleList* /* list */) const override{};
Start()179 void Start() override { log::error("Started TestStackDumpsysBase"); };
Stop()180 void Stop() override { log::error("Stopped TestStackDumpsysBase"); };
ToString() const181 std::string ToString() const override { return std::string("TestFunction"); }
182
183 TestStackDumpsysBase() = default;
184 };
185
186 struct StackRunningData {
187 std::function<void(bool is_running)> cb;
188 };
189
190 class TestStackDumpsys1 : public TestStackDumpsysBase {
191 public:
192 TestStackDumpsys1(const TestStackDumpsys1&) = delete;
193 TestStackDumpsys1& operator=(const TestStackDumpsys1&) = delete;
194 virtual ~TestStackDumpsys1() = default;
195
196 static const ModuleFactory Factory;
197
198 void TestMethod(TestData test_data) const override;
199 void IsStackRunning(StackRunningData stack_running_data) const;
200
201 private:
202 struct impl;
203 std::shared_ptr<impl> impl_;
204 TestStackDumpsys1();
205 };
206
207 struct TestStackDumpsys1::impl : public ModuleMainloop, public ModuleJniloop {
testTestStackDumpsys1::impl208 void test(TestData test_data) {
209 TestCallbackData callback_data{
210 .iter = test_data.iter,
211 .tag = std::string(__func__),
212 };
213 PostFunctionOnMain(
214 [](std::function<void(TestCallbackData callback_data)> callback,
215 TestCallbackData data) { callback(data); },
216 test_data.callback, callback_data);
217 }
is_stack_runningTestStackDumpsys1::impl218 void is_stack_running(StackRunningData stack_running_data) const {
219 bool is_running = bluetooth::shim::Stack::GetInstance()->IsRunning();
220 if (stack_running_data.cb) {
221 stack_running_data.cb(is_running);
222 }
223 }
224 };
225
TestStackDumpsys1()226 TestStackDumpsys1::TestStackDumpsys1() : TestStackDumpsysBase() {
227 impl_ = std::make_shared<impl>();
228 }
229
TestMethod(TestData test_data) const230 void TestStackDumpsys1::TestMethod(TestData test_data) const {
231 PostMethodOnMain(impl_, &impl::test, test_data);
232 }
233
IsStackRunning(StackRunningData stack_running_data) const234 void TestStackDumpsys1::IsStackRunning(
235 StackRunningData stack_running_data) const {
236 GetHandler()->CallOn(impl_.get(), &impl::is_stack_running,
237 stack_running_data);
238 }
239
240 class TestStackDumpsys2 : public TestStackDumpsysBase {
241 public:
242 TestStackDumpsys2(const TestStackDumpsys2&) = delete;
243 TestStackDumpsys2& operator=(const TestStackDumpsys2&) = delete;
244 virtual ~TestStackDumpsys2() = default;
245
246 static const ModuleFactory Factory;
247
248 void TestMethod(TestData test_data) const override;
249
250 private:
251 struct impl;
252 std::shared_ptr<impl> impl_;
253 TestStackDumpsys2();
254 };
255
256 struct TestStackDumpsys2::impl : public ModuleMainloop, public ModuleJniloop {
testTestStackDumpsys2::impl257 void test(TestData test_data) {
258 TestCallbackData callback_data{
259 .iter = test_data.iter,
260 .tag = std::string(__func__),
261 };
262 PostFunctionOnMain(
263 [](std::function<void(TestCallbackData callback_data)> callback,
264 TestCallbackData data) { callback(data); },
265 test_data.callback, callback_data);
266 }
267 };
268
TestStackDumpsys2()269 TestStackDumpsys2::TestStackDumpsys2() : TestStackDumpsysBase() {
270 impl_ = std::make_shared<impl>();
271 }
272
TestMethod(TestData test_data) const273 void TestStackDumpsys2::TestMethod(TestData test_data) const {
274 PostMethodOnMain(impl_, &impl::test, test_data);
275 }
276
277 class TestStackDumpsys3 : public TestStackDumpsysBase {
278 public:
279 TestStackDumpsys3(const TestStackDumpsys3&) = delete;
280 TestStackDumpsys3& operator=(const TestStackDumpsys3&) = delete;
281 virtual ~TestStackDumpsys3() = default;
282
283 static const ModuleFactory Factory;
284
285 void TestMethod(TestData test_data) const override;
286
287 private:
288 struct impl;
289 std::shared_ptr<impl> impl_;
290 TestStackDumpsys3();
291 };
292
293 struct TestStackDumpsys3::impl : public ModuleMainloop, public ModuleJniloop {
testTestStackDumpsys3::impl294 void test(TestData test_data) {
295 TestCallbackData callback_data{
296 .iter = test_data.iter,
297 .tag = std::string(__func__),
298 };
299 PostFunctionOnMain(
300 [](std::function<void(TestCallbackData callback_data)> callback,
301 TestCallbackData data) { callback(data); },
302 test_data.callback, callback_data);
303 }
304 };
305
TestStackDumpsys3()306 TestStackDumpsys3::TestStackDumpsys3() : TestStackDumpsysBase() {
307 impl_ = std::make_shared<impl>();
308 }
309
TestMethod(TestData test_data) const310 void TestStackDumpsys3::TestMethod(TestData test_data) const {
311 PostMethodOnMain(impl_, &impl::test, test_data);
312 }
313
314 class TestStackDumpsys4 : public TestStackDumpsysBase {
315 public:
316 TestStackDumpsys4(const TestStackDumpsys4&) = delete;
317 TestStackDumpsys4& operator=(const TestStackDumpsys3&) = delete;
318 virtual ~TestStackDumpsys4() = default;
319
320 static const ModuleFactory Factory;
321
TestMethod(TestData test_data) const322 void TestMethod(TestData test_data) const override {
323 log::info("mod:{} iter:{} tag:{}", __func__, test_data.iter, test_data.tag);
324 }
325
326 private:
327 struct impl;
328 std::shared_ptr<impl> impl_;
TestStackDumpsys4()329 TestStackDumpsys4() : TestStackDumpsysBase() {}
330 };
331
332 struct TestStackDumpsys4::impl : public ModuleMainloop, public ModuleJniloop {};
333
334 const ModuleFactory TestStackDumpsysBase::Factory =
__anonc942baa20802() 335 ModuleFactory([]() { return new TestStackDumpsysBase(); });
336
337 const ModuleFactory TestStackDumpsys1::Factory =
__anonc942baa20902() 338 ModuleFactory([]() { return new TestStackDumpsys1(); });
339 const ModuleFactory TestStackDumpsys2::Factory =
__anonc942baa20a02() 340 ModuleFactory([]() { return new TestStackDumpsys2(); });
341 const ModuleFactory TestStackDumpsys3::Factory =
__anonc942baa20b02() 342 ModuleFactory([]() { return new TestStackDumpsys3(); });
343 const ModuleFactory TestStackDumpsys4::Factory =
__anonc942baa20c02() 344 ModuleFactory([]() { return new TestStackDumpsys4(); });
345
346 class StackWithMainThreadUnitTest : public ::testing::Test {
347 protected:
SetUp()348 void SetUp() override { main_thread_ = std::make_unique<MainThread>(); }
TearDown()349 void TearDown() override { main_thread_.reset(); }
350
351 private:
352 std::unique_ptr<MainThread> main_thread_;
353 };
354
355 class StackLifecycleUnitTest : public StackWithMainThreadUnitTest {
356 public:
StackManager() const357 std::shared_ptr<TestStackManager> StackManager() const {
358 return stack_manager_;
359 }
360
361 protected:
SetUp()362 void SetUp() override {
363 StackWithMainThreadUnitTest::SetUp();
364 stack_manager_ = std::make_shared<TestStackManager>();
365 }
366
TearDown()367 void TearDown() override {
368 stack_manager_.reset();
369 StackWithMainThreadUnitTest::TearDown();
370 }
371
372 private:
373 std::shared_ptr<TestStackManager> stack_manager_;
374 };
375
376 class MainShimStackDumpsysTest : public StackLifecycleUnitTest {
377 protected:
SetUp()378 void SetUp() override {
379 StackLifecycleUnitTest::SetUp();
380 StackManager()->AddModule<TestStackDumpsys1>();
381 StackManager()->AddModule<TestStackDumpsys2>();
382 StackManager()->AddModule<TestStackDumpsys3>();
383 StackManager()->AddModule<bluetooth::shim::Dumpsys>();
384 StackManager()->Start();
385 ASSERT_EQ(4U, StackManager()->NumModules());
386
387 bluetooth::shim::RegisterDumpsysFunction((void*)this, [](int fd) {
388 log::info("Callback to dump legacy data fd:{}", fd);
389 });
390 }
391
TearDown()392 void TearDown() override {
393 bluetooth::shim::UnregisterDumpsysFunction((void*)this);
394 StackLifecycleUnitTest::TearDown();
395 }
396 };
397
398 struct CallablePostCnt {
399 size_t success{0};
400 size_t misses{0};
operator +=CallablePostCnt401 CallablePostCnt operator+=(const CallablePostCnt& post_cnt) {
402 return CallablePostCnt(
403 {success += post_cnt.success, misses += post_cnt.misses});
404 }
405 };
406
407 // Provide a client user of the stack manager module services
408 class Client {
409 public:
Client(int id)410 Client(int id) : id_(id) {}
411 Client(const Client&) = default;
412 virtual ~Client() = default;
413
414 // Start up the client a thread and handler
Start()415 void Start() {
416 thread_ = new os::Thread(common::StringFormat("ClientThread%d", id_),
417 os::Thread::Priority::NORMAL);
418 handler_ = new os::Handler(thread_);
419 handler_->Post(common::BindOnce(
420 [](int id) { log::info("<=== tid Started client id:{}", id); }, id_));
421 }
422
423 // Ensure all the client handlers are running
Await()424 void Await() {
425 std::promise<void> promise;
426 std::future future = promise.get_future();
427 handler_->Post(
428 base::BindOnce([](std::promise<void> promise) { promise.set_value(); },
429 std::move(promise)));
430 future.wait();
431 }
432
433 // Post a work task on behalf of this client
Post(common::OnceClosure closure)434 void Post(common::OnceClosure closure) {
435 if (quiesced_) {
436 post_cnt_.misses++;
437 } else {
438 post_cnt_.success++;
439 handler_->Post(std::move(closure));
440 }
441 }
442
443 // Safely prevent new work tasks from being posted
Quiesce()444 void Quiesce() {
445 if (quiesced_) return;
446 quiesced_ = true;
447 std::promise promise = std::promise<void>();
448 std::future future = promise.get_future();
449 handler_->Post(common::BindOnce(
450 [](std::promise<void> promise, int id) {
451 promise.set_value();
452 log::info("<=== tid Quiesced client id:{}", id);
453 },
454 std::move(promise), id_));
455 future.wait_for(std::chrono::milliseconds(kSyncMainLoopTimeoutMs));
456 }
457
458 // Queisces if needed and stops the client then releases associated resources
Stop()459 void Stop() {
460 if (!quiesced_) {
461 Quiesce();
462 }
463 handler_->Clear();
464 handler_->WaitUntilStopped(
465 std::chrono::milliseconds(kWaitUntilHandlerStoppedMs));
466 delete handler_;
467 delete thread_;
468 }
469
Id() const470 int Id() const { return id_; }
471
GetCallablePostCnt() const472 CallablePostCnt GetCallablePostCnt() const { return post_cnt_; }
473
Name() const474 std::string Name() const {
475 return common::StringFormat("%s%d", __func__, id_);
476 }
477
478 private:
479 int id_{0};
480 CallablePostCnt post_cnt_{};
481 bool quiesced_{false};
482 os::Handler* handler_{nullptr};
483 os::Thread* thread_{nullptr};
484 };
485
486 // Convenience object to handle multiple clients with logging
487 class ClientGroup {
488 public:
ClientGroup(size_t num_clients)489 explicit ClientGroup(size_t num_clients) {
490 for (size_t i = 0; i < num_clients; i++) {
491 clients_.emplace_back(std::make_unique<Client>(i));
492 }
493 }
494
Start()495 void Start() {
496 log_tag(kLogTagStarting);
497 for (auto& c : clients_) {
498 c->Start();
499 }
500 }
501
Await()502 void Await() {
503 for (auto& c : clients_) {
504 c->Await();
505 }
506 log_tag(kLogTagStarted);
507 }
508
Quiesce()509 void Quiesce() {
510 log_tag(kLogTagQuiescing);
511 for (auto& c : clients_) {
512 c->Quiesce();
513 }
514 log_tag(kLogTagQuiesced);
515 }
516
Stop()517 void Stop() {
518 for (auto& c : clients_) {
519 c->Stop();
520 }
521 log_tag(kLogTagStopped);
522 }
523
Dump() const524 void Dump() const {
525 for (auto& c : clients_) {
526 log::info("Callable post cnt client_id:{} success:{} misses:{}", c->Id(),
527 c->GetCallablePostCnt().success,
528 c->GetCallablePostCnt().misses);
529 }
530 }
531
GetCallablePostCnt() const532 CallablePostCnt GetCallablePostCnt() const {
533 CallablePostCnt post_cnt{};
534 for (auto& c : clients_) {
535 post_cnt += c->GetCallablePostCnt();
536 }
537 return post_cnt;
538 }
539
NumClients() const540 size_t NumClients() const { return clients_.size(); }
541
542 std::vector<std::unique_ptr<Client>> clients_;
543 };
544
545 class MainShimStackDumpsysWithClientsTest : public MainShimStackDumpsysTest {
546 protected:
SetUp()547 void SetUp() override {
548 MainShimStackDumpsysTest::SetUp();
549 client_group_.Start();
550 client_group_.Await();
551 }
552
TearDown()553 void TearDown() override {
554 client_group_.Quiesce();
555 client_group_.Stop();
556 MainShimStackDumpsysTest::TearDown();
557 }
558 ClientGroup client_group_ = ClientGroup(kNumTestClients);
559 };
560
TEST_F(MainShimStackDumpsysWithClientsTest,all_clients_check_stack_running)561 TEST_F(MainShimStackDumpsysWithClientsTest, all_clients_check_stack_running) {
562 StackRunningData stack_running_data = {
563 .cb =
564 [](bool is_stack_running) {
565 log::info("Stack is running:{}", (is_stack_running) ? 'T' : 'F');
566 },
567 };
568
569 // Ensure the dumpsys instance is included within the stack
570 ASSERT_NE(nullptr, bluetooth::shim::GetDumpsys());
571
572 for (auto& c : client_group_.clients_) {
573 c->Post(base::BindOnce(
574 [](StackRunningData stack_running_data) {
575 bluetooth::shim::Stack::GetInstance()
576 ->GetStackManager()
577 ->GetInstance<TestStackDumpsys1>()
578 ->IsStackRunning(stack_running_data);
579 },
580 stack_running_data));
581 }
582 }
583
TEST_F(MainShimStackDumpsysWithClientsTest,all_clients_check_stack_running_with_iterations)584 TEST_F(MainShimStackDumpsysWithClientsTest,
585 all_clients_check_stack_running_with_iterations) {
586 StackRunningData stack_running_data = {
587 .cb =
588 [](bool is_stack_running) {
589 log::info("Run on mainloop: Stack is running:{}",
590 (is_stack_running) ? 'T' : 'F');
591 },
592 };
593
594 // Ensure the dumpsys instance is included within the stack
595 ASSERT_NE(nullptr, bluetooth::shim::GetDumpsys());
596
597 for (int i = 0; i < 2; i++) {
598 log::info("Iteration:{}", i);
599 for (auto& c : client_group_.clients_) {
600 c->Post(base::BindOnce(
601 [](StackRunningData stack_running_data) {
602 bluetooth::shim::Stack::GetInstance()
603 ->GetStackManager()
604 ->GetInstance<TestStackDumpsys1>()
605 ->IsStackRunning(stack_running_data);
606 },
607 stack_running_data));
608 }
609 }
610 }
611
TEST_F(MainShimStackDumpsysWithClientsTest,dumpsys_single_client)612 TEST_F(MainShimStackDumpsysWithClientsTest, dumpsys_single_client) {
613 // Ensure the dumpsys instance is included within the stack
614 ASSERT_NE(nullptr, bluetooth::shim::GetDumpsys());
615
616 const int fd = 1;
617 client_group_.clients_[0]->Post(
618 base::BindOnce([](int fd) { bluetooth::shim::Dump(fd, nullptr); }, fd));
619 }
620
TEST_F(MainShimStackDumpsysWithClientsTest,dumpsys_single_client_with_running_check)621 TEST_F(MainShimStackDumpsysWithClientsTest,
622 dumpsys_single_client_with_running_check) {
623 StackRunningData stack_running_data = {
624 .cb =
625 [](bool is_stack_running) {
626 log::info("Stack is running:{}", (is_stack_running) ? 'T' : 'F');
627 },
628 };
629
630 // Ensure the dumpsys instance is included within the stack
631 ASSERT_NE(nullptr, bluetooth::shim::GetDumpsys());
632
633 const int fd = 1;
634 client_group_.clients_[0]->Post(base::BindOnce(
635 [](StackRunningData stack_running_data) {
636 bluetooth::shim::Stack::GetInstance()
637 ->GetStackManager()
638 ->GetInstance<TestStackDumpsys1>()
639 ->IsStackRunning(stack_running_data);
640 },
641 stack_running_data));
642 client_group_.clients_[0]->Post(
643 base::BindOnce([](int fd) { bluetooth::shim::Dump(fd, nullptr); }, fd));
644 }
645
TEST_F(MainShimStackDumpsysWithClientsTest,dumpsys_many_clients)646 TEST_F(MainShimStackDumpsysWithClientsTest, dumpsys_many_clients) {
647 StackRunningData stack_running_data = {
648 .cb =
649 [](bool is_stack_running) {
650 log::info("Stack is running:{}", (is_stack_running) ? 'T' : 'F');
651 },
652 };
653
654 const int fd = 1;
655 for (auto& c : client_group_.clients_) {
656 c->Post(
657 base::BindOnce([](int fd) { bluetooth::shim::Dump(fd, nullptr); }, fd));
658 }
659 }
660