1 /*
2 * Copyright (C) 2022 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 <cstdint>
18
19 #include "chre/pal/ble.h"
20 #include "chre/platform/condition_variable.h"
21 #include "chre/platform/linux/task_util/task_manager.h"
22 #include "chre/platform/log.h"
23 #include "chre/platform/mutex.h"
24 #include "chre/platform/shared/pal_system_api.h"
25 #include "chre/util/fixed_size_vector.h"
26 #include "chre/util/lock_guard.h"
27 #include "chre/util/macros.h"
28 #include "chre/util/nanoapp/ble.h"
29 #include "chre/util/optional.h"
30 #include "chre/util/unique_ptr.h"
31 #include "gmock/gmock.h"
32 #include "gtest/gtest.h"
33
34 namespace {
35
36 using ::chre::ConditionVariable;
37 using ::chre::createBleScanFilterForKnownBeacons;
38 using ::chre::createBleScanFilterForKnownBeaconsV1_9;
39 using ::chre::FixedSizeVector;
40 using ::chre::gChrePalSystemApi;
41 using ::chre::LockGuard;
42 using ::chre::MakeUnique;
43 using ::chre::Milliseconds;
44 using ::chre::Mutex;
45 using ::chre::Nanoseconds;
46 using ::chre::Optional;
47 using ::chre::Seconds;
48 using ::chre::UniquePtr;
49 using ::chre::ble_constants::kNumScanFilters;
50
51 const Nanoseconds kBleStatusTimeoutNs = Milliseconds(200);
52 const Nanoseconds kBleEventTimeoutNs = Seconds(10);
53 constexpr uint32_t kBleBatchDurationMs = 0;
54
55 class Callbacks {
56 public:
57 Callbacks() = delete;
58
Callbacks(const struct chrePalBleApi * api)59 explicit Callbacks(const struct chrePalBleApi *api) : mApi(api) {}
60
requestStateResync()61 void requestStateResync() {}
62
scanStatusChangeCallback(bool enabled,uint8_t errorCode)63 void scanStatusChangeCallback(bool enabled, uint8_t errorCode) {
64 LOGI("Received scan status change with enabled %d error %d", enabled,
65 errorCode);
66 LockGuard<Mutex> lock(mMutex);
67 mEnabled = enabled;
68 mCondVarStatus.notify_one();
69 }
70
advertisingEventCallback(struct chreBleAdvertisementEvent * event)71 void advertisingEventCallback(struct chreBleAdvertisementEvent *event) {
72 LOGI("Received advertising event");
73 LockGuard<Mutex> lock(mMutex);
74 if (!mEventData.full()) {
75 mEventData.push_back(event);
76 if (mEventData.full()) {
77 mCondVarEvents.notify_one();
78 }
79 } else {
80 mApi->releaseAdvertisingEvent(event);
81 }
82 }
83
84 Optional<bool> mEnabled;
85
86 static constexpr uint32_t kNumEvents = 3;
87 FixedSizeVector<struct chreBleAdvertisementEvent *, kNumEvents> mEventData;
88
89 //! Synchronize access to class members.
90 Mutex mMutex;
91 ConditionVariable mCondVarStatus;
92 ConditionVariable mCondVarEvents;
93
94 //! CHRE PAL implementation API.
95 const struct chrePalBleApi *mApi;
96 };
97
98 UniquePtr<Callbacks> gCallbacks = nullptr;
99
requestStateResync()100 void requestStateResync() {
101 if (gCallbacks != nullptr) {
102 gCallbacks->requestStateResync();
103 }
104 }
105
scanStatusChangeCallback(bool enabled,uint8_t errorCode)106 void scanStatusChangeCallback(bool enabled, uint8_t errorCode) {
107 if (gCallbacks != nullptr) {
108 gCallbacks->scanStatusChangeCallback(enabled, errorCode);
109 }
110 }
111
advertisingEventCallback(struct chreBleAdvertisementEvent * event)112 void advertisingEventCallback(struct chreBleAdvertisementEvent *event) {
113 if (gCallbacks != nullptr) {
114 gCallbacks->advertisingEventCallback(event);
115 }
116 }
117
118 class PalBleTest : public testing::Test {
119 protected:
SetUp()120 void SetUp() override {
121 chre::TaskManagerSingleton::deinit();
122 chre::TaskManagerSingleton::init();
123 mApi = chrePalBleGetApi(CHRE_PAL_BLE_API_CURRENT_VERSION);
124 gCallbacks = MakeUnique<Callbacks>(mApi);
125 ASSERT_NE(mApi, nullptr);
126 EXPECT_EQ(mApi->moduleVersion, CHRE_PAL_BLE_API_CURRENT_VERSION);
127 ASSERT_TRUE(mApi->open(&gChrePalSystemApi, &mPalCallbacks));
128 }
129
TearDown()130 void TearDown() override {
131 if (mApi != nullptr) {
132 mApi->close();
133 }
134 chre::TaskManagerSingleton::deinit();
135 gCallbacks = nullptr;
136 }
137
createBleGenericFilter(uint8_t type,uint8_t len,uint8_t * data,uint8_t * mask)138 chreBleGenericFilter createBleGenericFilter(uint8_t type, uint8_t len,
139 uint8_t *data, uint8_t *mask) {
140 chreBleGenericFilter filter;
141 memset(&filter, 0, sizeof(filter));
142 filter.type = type;
143 filter.len = len;
144 memcpy(filter.data, data, sizeof(uint8_t) * len);
145 memcpy(filter.dataMask, mask, sizeof(uint8_t) * len);
146 return filter;
147 }
148
149 //! CHRE PAL implementation API.
150 const struct chrePalBleApi *mApi;
151
152 const struct chrePalBleCallbacks mPalCallbacks = {
153 .requestStateResync = requestStateResync,
154 .scanStatusChangeCallback = scanStatusChangeCallback,
155 .advertisingEventCallback = advertisingEventCallback,
156 };
157 };
158
TEST_F(PalBleTest,Capabilities)159 TEST_F(PalBleTest, Capabilities) {
160 auto caps = mApi->getCapabilities();
161 LOGI("capabilities: 0x%x", caps);
162 EXPECT_NE(caps, 0);
163 EXPECT_EQ(caps & ~(CHRE_BLE_CAPABILITIES_SCAN |
164 CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT |
165 CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING |
166 CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT),
167 0);
168
169 auto filter_caps = mApi->getFilterCapabilities();
170 LOGI("filter capabilities: 0x%x", filter_caps);
171 EXPECT_NE(filter_caps, 0);
172 EXPECT_EQ(filter_caps & ~(CHRE_BLE_FILTER_CAPABILITIES_RSSI |
173 CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA),
174 0);
175 }
176
177 // NB: To pass this test, it is required to have an external BLE device
178 // advertising BLE beacons with service data for either the Google eddystone
179 // or fastpair UUIDs.
TEST_F(PalBleTest,FilteredScan)180 TEST_F(PalBleTest, FilteredScan) {
181 struct chreBleScanFilterV1_9 filterV1_9;
182 chreBleGenericFilter uuidFilters[kNumScanFilters];
183 createBleScanFilterForKnownBeaconsV1_9(filterV1_9, uuidFilters,
184 kNumScanFilters);
185
186 LockGuard<Mutex> lock(gCallbacks->mMutex);
187
188 EXPECT_TRUE(mApi->startScan(CHRE_BLE_SCAN_MODE_BACKGROUND,
189 kBleBatchDurationMs, &filterV1_9));
190
191 EXPECT_TRUE(mApi->startScan(CHRE_BLE_SCAN_MODE_AGGRESSIVE,
192 kBleBatchDurationMs, &filterV1_9));
193 gCallbacks->mCondVarStatus.wait_for(gCallbacks->mMutex, kBleStatusTimeoutNs);
194 ASSERT_TRUE(gCallbacks->mEnabled.has_value());
195 EXPECT_TRUE(gCallbacks->mEnabled.value());
196
197 gCallbacks->mCondVarEvents.wait_for(gCallbacks->mMutex, kBleEventTimeoutNs);
198 EXPECT_TRUE(gCallbacks->mEventData.full());
199 for (auto event : gCallbacks->mEventData) {
200 // TODO(b/249577259): validate event data
201 mApi->releaseAdvertisingEvent(event);
202 }
203
204 EXPECT_TRUE(mApi->stopScan());
205 }
206
207 } // namespace