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