• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/gatt/generic_attribute_service.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
19 #include "pw_bluetooth_sapphire/internal/host/gatt/persisted_data.h"
20 #include "pw_unit_test/framework.h"
21 
22 namespace bt::gatt {
23 namespace {
24 // Handles for the third attribute (Service Changed characteristic) and fourth
25 // attribute (corresponding client config).
26 constexpr att::Handle kChrcHandle = 0x0003;
27 constexpr att::Handle kCCCHandle = 0x0004;
28 constexpr PeerId kTestPeerId(1);
29 constexpr uint16_t kEnableInd = 0x0002;
30 
31 class GenericAttributeServiceTest : public ::testing::Test {
32  protected:
WriteServiceChangedCcc(PeerId peer_id,uint16_t ccc_value,fit::result<att::ErrorCode> * out_status)33   bool WriteServiceChangedCcc(PeerId peer_id,
34                               uint16_t ccc_value,
35                               fit::result<att::ErrorCode>* out_status) {
36     BT_ASSERT(out_status);
37 
38     auto* attr = mgr.database()->FindAttribute(kCCCHandle);
39     BT_ASSERT(attr);
40     auto result_cb = [&out_status](auto cb_status) { *out_status = cb_status; };
41     uint16_t value = htole16(ccc_value);
42     return attr->WriteAsync(
43         peer_id, 0u, BufferView(&value, sizeof(value)), result_cb);
44   }
45 
46   LocalServiceManager mgr;
47 };
48 
49 // Test registration and unregistration of the local GATT service itself.
TEST_F(GenericAttributeServiceTest,RegisterUnregister)50 TEST_F(GenericAttributeServiceTest, RegisterUnregister) {
51   {
52     GenericAttributeService gatt_service(mgr.GetWeakPtr(), NopSendIndication);
53 
54     // Check that the local attribute database has a grouping for the GATT GATT
55     // service with four attributes.
56     auto iter = mgr.database()->groupings().begin();
57     EXPECT_TRUE(iter->complete());
58     EXPECT_EQ(4u, iter->attributes().size());
59     EXPECT_TRUE(iter->active());
60     EXPECT_EQ(0x0001, iter->start_handle());
61     EXPECT_EQ(0x0004, iter->end_handle());
62     EXPECT_EQ(types::kPrimaryService, iter->group_type());
63 
64     auto const* ccc_attr = mgr.database()->FindAttribute(kCCCHandle);
65     ASSERT_TRUE(ccc_attr != nullptr);
66     EXPECT_EQ(types::kClientCharacteristicConfig, ccc_attr->type());
67   }
68 
69   // The service should now be unregistered, so no characeteristic attributes
70   // should be active.
71   auto const* chrc_attr = mgr.database()->FindAttribute(kChrcHandle);
72   ASSERT_TRUE(chrc_attr == nullptr);
73 }
74 
75 // Tests that registering the GATT service, enabling indication on its Service
76 // Changed characteristic, then registering a different service invokes the
77 // callback to send an indication to the "client."
TEST_F(GenericAttributeServiceTest,IndicateOnRegister)78 TEST_F(GenericAttributeServiceTest, IndicateOnRegister) {
79   std::optional<IdType> indicated_svc_id;
80   auto send_indication =
81       [&](IdType service_id, IdType chrc_id, PeerId peer_id, BufferView value) {
82         EXPECT_EQ(kTestPeerId, peer_id);
83         EXPECT_EQ(kServiceChangedChrcId, chrc_id);
84         indicated_svc_id = service_id;
85 
86         ASSERT_EQ(4u, value.size());
87         // The second service following the four-attribute GATT service should
88         // span the subsequent three handles.
89         EXPECT_EQ(0x05, value[0]);
90         EXPECT_EQ(0x00, value[1]);
91         EXPECT_EQ(0x07, value[2]);
92         EXPECT_EQ(0x00, value[3]);
93       };
94 
95   // Register the GATT service.
96   GenericAttributeService gatt_service(mgr.GetWeakPtr(),
97                                        std::move(send_indication));
98 
99   // Enable Service Changed indications for the test client.
100   fit::result<att::ErrorCode> status = fit::ok();
101   WriteServiceChangedCcc(kTestPeerId, kEnableInd, &status);
102   EXPECT_EQ(std::nullopt, indicated_svc_id);
103 
104   constexpr UUID kTestSvcType(uint32_t{0xdeadbeef});
105   constexpr IdType kChrcId = 12;
106   constexpr uint8_t kChrcProps = Property::kRead;
107   constexpr UUID kTestChrcType(uint32_t{0xdeadbeef});
108   const att::AccessRequirements kReadReqs(/*encryption=*/true,
109                                           /*authentication=*/true,
110                                           /*authorization=*/true);
111   const att::AccessRequirements kWriteReqs, kUpdateReqs;
112   auto service = std::make_unique<Service>(/*primary=*/false, kTestSvcType);
113   service->AddCharacteristic(std::make_unique<Characteristic>(kChrcId,
114                                                               kTestChrcType,
115                                                               kChrcProps,
116                                                               0,
117                                                               kReadReqs,
118                                                               kWriteReqs,
119                                                               kUpdateReqs));
120   auto service_id = mgr.RegisterService(
121       std::move(service), NopReadHandler, NopWriteHandler, NopCCCallback);
122   // Verify that service registration succeeded
123   EXPECT_NE(kInvalidId, service_id);
124   // Verify that |send_indication| was invoked to indicate the Service Changed
125   // chrc within the gatt_service.
126   EXPECT_EQ(gatt_service.service_id(), indicated_svc_id);
127 }
128 
129 // Same test as above, but the indication is enabled just prior unregistering
130 // the latter test service.
TEST_F(GenericAttributeServiceTest,IndicateOnUnregister)131 TEST_F(GenericAttributeServiceTest, IndicateOnUnregister) {
132   std::optional<IdType> indicated_svc_id;
133   auto send_indication =
134       [&](IdType service_id, IdType chrc_id, PeerId peer_id, BufferView value) {
135         EXPECT_EQ(kTestPeerId, peer_id);
136         EXPECT_EQ(kServiceChangedChrcId, chrc_id);
137         indicated_svc_id = service_id;
138 
139         ASSERT_EQ(4u, value.size());
140         // The second service following the four-attribute GATT service should
141         // span the subsequent four handles (update enabled).
142         EXPECT_EQ(0x05, value[0]);
143         EXPECT_EQ(0x00, value[1]);
144         EXPECT_EQ(0x08, value[2]);
145         EXPECT_EQ(0x00, value[3]);
146       };
147 
148   // Register the GATT service.
149   GenericAttributeService gatt_service(mgr.GetWeakPtr(),
150                                        std::move(send_indication));
151 
152   constexpr UUID kTestSvcType(uint32_t{0xdeadbeef});
153   constexpr IdType kChrcId = 0;
154   constexpr uint8_t kChrcProps = Property::kNotify;
155   constexpr UUID kTestChrcType(uint32_t{0xdeadbeef});
156   const att::AccessRequirements kReadReqs, kWriteReqs;
157   const att::AccessRequirements kUpdateReqs(/*encryption=*/true,
158                                             /*authentication=*/true,
159                                             /*authorization=*/true);
160   auto service = std::make_unique<Service>(/*primary=*/false, kTestSvcType);
161   service->AddCharacteristic(std::make_unique<Characteristic>(kChrcId,
162                                                               kTestChrcType,
163                                                               kChrcProps,
164                                                               0,
165                                                               kReadReqs,
166                                                               kWriteReqs,
167                                                               kUpdateReqs));
168   auto service_id = mgr.RegisterService(
169       std::move(service), NopReadHandler, NopWriteHandler, NopCCCallback);
170   // Verify that service registration succeeded
171   EXPECT_NE(kInvalidId, service_id);
172 
173   // Enable Service Changed indications for the test client.
174   fit::result<att::ErrorCode> status = fit::ok();
175   WriteServiceChangedCcc(kTestPeerId, kEnableInd, &status);
176   EXPECT_EQ(std::nullopt, indicated_svc_id);
177 
178   mgr.UnregisterService(service_id);
179   // Verify that |send_indication| was invoked to indicate the Service Changed
180   // chrc within the gatt_service.
181   EXPECT_EQ(gatt_service.service_id(), indicated_svc_id);
182 }
183 
184 // Tests that registering the GATT service reads a persisted value for the
185 // service changed characteristic's ccc, and that enabling indication on its
186 // service changed characteristic writes a persisted value.
TEST_F(GenericAttributeServiceTest,PersistIndicate)187 TEST_F(GenericAttributeServiceTest, PersistIndicate) {
188   int persist_callback_count = 0;
189 
190   auto persist_callback = [&persist_callback_count](
191                               PeerId peer_id,
192                               ServiceChangedCCCPersistedData gatt_data) {
193     EXPECT_EQ(peer_id, kTestPeerId);
194     EXPECT_EQ(gatt_data.indicate, true);
195     persist_callback_count++;
196   };
197 
198   // Register the GATT service.
199   GenericAttributeService gatt_service(mgr.GetWeakPtr(), NopSendIndication);
200   gatt_service.SetPersistServiceChangedCCCCallback(std::move(persist_callback));
201   EXPECT_EQ(persist_callback_count, 0);
202 
203   // Enable Service Changed indications for the test client.
204   fit::result<att::ErrorCode> status = fit::ok();
205   WriteServiceChangedCcc(kTestPeerId, kEnableInd, &status);
206   EXPECT_EQ(persist_callback_count, 1);
207 }
208 }  // namespace
209 }  // namespace bt::gatt
210