Skip to content

Commit efd151c

Browse files
Lucas JenkinsCQ Bot
authored andcommitted
[bt][gatt] Clean up MTU exchange result handling
Instead of returning a tuple of (att::Result<>, mtu) from gatt::Client:: ExchangeMTU's MTUCallback, return an att::Result<mtu>. This represents the result of the procedure more accurately - if the MTU exchange fails, no MTU is negotiated. The one edge case is if the MTU exchange fails because the peer does not support the procedure. This procedure is optional per v5.3 Vol. 3 Part F Table 4.1, and in this case we should continue initialization with the understanding that the MTU is the default, minimum LE MTU. This is the only behavior change from the prior code, the rest of the change is effectively a refactor. Also, change some EXPECT_EQ->ASSERT_EQ in RemoteServiceManagerTests so that failed expectations do not cause invalid array accesses and process termination. Bug: 36375 Test: `fx test bt-host-gatt-tests`, refactored gatt/client_unittests.cc and added RemoteServiceManagerTest.InitializeMtuExchangeNotSupportedSucceeds Change-Id: Ife0cb82eb70066cd8fc3988ac936d97fad71b8fd Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/689574 Reviewed-by: Ben Lawson <[email protected]> Commit-Queue: Lucas Jenkins <[email protected]> Fuchsia-Auto-Submit: Lucas Jenkins <[email protected]>
1 parent 56d5159 commit efd151c

File tree

6 files changed

+107
-93
lines changed

6 files changed

+107
-93
lines changed

src/connectivity/bluetooth/core/bt-host/gatt/client.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,16 @@ class Impl final : public Client {
137137
void ExchangeMTU(MTUCallback mtu_cb) override {
138138
auto pdu = NewPDU(sizeof(att::ExchangeMTURequestParams));
139139
if (!pdu) {
140-
mtu_cb(ToResult(HostError::kOutOfMemory), 0);
140+
mtu_cb(fitx::error(att::Error(HostError::kOutOfMemory)));
141141
return;
142142
}
143143

144144
att::PacketWriter writer(att::kExchangeMTURequest, pdu.get());
145145
auto params = writer.mutable_payload<att::ExchangeMTURequestParams>();
146146
params->client_rx_mtu = htole16(att_->preferred_mtu());
147147

148-
auto rsp_cb = [this, mtu_cb = std::move(mtu_cb)](att::Bearer::TransactionResult result) {
148+
auto rsp_cb = [this,
149+
mtu_cb = std::move(mtu_cb)](att::Bearer::TransactionResult result) mutable {
149150
if (result.is_ok()) {
150151
const att::PacketReader& rsp = result.value();
151152
ZX_DEBUG_ASSERT(rsp.opcode() == att::kExchangeMTUResponse);
@@ -154,7 +155,7 @@ class Impl final : public Client {
154155
// Received a malformed response. Disconnect the link.
155156
att_->ShutDown();
156157

157-
mtu_cb(ToResult(HostError::kPacketMalformed), 0);
158+
mtu_cb(fitx::error(att::Error(HostError::kPacketMalformed)));
158159
return;
159160
}
160161

@@ -166,7 +167,7 @@ class Impl final : public Client {
166167
uint16_t final_mtu = std::max(att::kLEMinMTU, std::min(server_mtu, att_->preferred_mtu()));
167168
att_->set_mtu(final_mtu);
168169

169-
mtu_cb(fitx::ok(), final_mtu);
170+
mtu_cb(fitx::ok(final_mtu));
170171
return;
171172
}
172173
const auto& [error, handle] = result.error_value();
@@ -177,12 +178,12 @@ class Impl final : public Client {
177178
if (error.is(att::ErrorCode::kRequestNotSupported)) {
178179
bt_log(DEBUG, "gatt", "peer does not support MTU exchange: using default");
179180
att_->set_mtu(att::kLEMinMTU);
180-
mtu_cb(fitx::error(error), att::kLEMinMTU);
181+
mtu_cb(fitx::error(error));
181182
return;
182183
}
183184

184185
bt_log(DEBUG, "gatt", "MTU exchange failed: %s", bt_str(error));
185-
mtu_cb(fitx::error(error), 0);
186+
mtu_cb(fitx::error(error));
186187
};
187188

188189
att_->StartTransaction(std::move(pdu), BindCallback(std::move(rsp_cb)));

src/connectivity/bluetooth/core/bt-host/gatt/client.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,19 @@ class Client {
3535
// Returns the current ATT MTU.
3636
virtual uint16_t mtu() const = 0;
3737

38-
// Initiates an MTU exchange and adjusts the MTU of the bearer according to
39-
// what the peer is capable of. The request will be initiated using the
40-
// bearer's preferred MTU.
38+
// Initiates an MTU exchange and adjusts the bearer's MTU as outlined in Core Spec v5.3 Vol. 3
39+
// Part F 3.4.2. The request will be made using the locally-preferred MTU.
4140
//
42-
// After the exchange is complete, the bearer will be updated to use the
43-
// resulting MTU. The resulting MTU will be notified via |callback|.
41+
// Upon successful exchange, the bearer will be updated to use the resulting MTU and |callback|
42+
// will be notified with the the resulting MTU.
4443
//
45-
// |status| will be set to an error if the MTU exchange fails. The |mtu|
46-
// parameter will be set to 0 and the underlying bearer's MTU will remain
47-
// unmodified.
48-
using MTUCallback = fit::function<void(att::Result<> status, uint16_t mtu)>;
44+
// MTU exchange support is optional per v5.3 Vol. 3 Part F Table 4.1, so if the exchange fails
45+
// because the peer doesn't support it, the bearer's MTU will be |kLEMinMTU| and initialization
46+
// should proceed. In this case, |mtu_result| will be |att::Error(att::kRequestNotSupported)|.
47+
//
48+
// If the MTU exchange otherwise fails, |mtu_result| will be an error and the bearer's MTU will
49+
// not change.
50+
using MTUCallback = fit::callback<void(att::Result<uint16_t> mtu_result)>;
4951
virtual void ExchangeMTU(MTUCallback callback) = 0;
5052

5153
// Performs a modified version of the "Discover All Primary Services" procedure defined in v5.0,

src/connectivity/bluetooth/core/bt-host/gatt/client_unittest.cc

Lines changed: 46 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "client.h"
66

7+
#include "src/connectivity/bluetooth/core/bt-host/att/att.h"
78
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
89
#include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_channel_test.h"
910

@@ -14,6 +15,14 @@ constexpr UUID kTestUuid1(uint16_t{0xDEAD});
1415
constexpr UUID kTestUuid2(uint16_t{0xBEEF});
1516
constexpr UUID kTestUuid3({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
1617

18+
att::Result<uint16_t> MtuResultFromErrCode(att::ErrorCode ecode) {
19+
return fitx::error(att::Error(ecode));
20+
}
21+
22+
att::Result<uint16_t> MtuResultFromHostErrCode(HostError ecode) {
23+
return fitx::error(att::Error(ecode));
24+
}
25+
1726
// clang-format off
1827
const StaticByteBuffer kDiscoverPrimaryRequest(
1928
0x10, // opcode: read by group type request
@@ -98,13 +107,8 @@ TEST_F(ClientTest, ExchangeMTUMalformedResponse) {
98107
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
99108
);
100109

101-
// Initialize to a non-zero value.
102-
uint16_t final_mtu = kPreferredMTU;
103-
att::Result<> status = fitx::ok();
104-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
105-
final_mtu = val;
106-
status = cb_status;
107-
};
110+
std::optional<att::Result<uint16_t>> result;
111+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
108112

109113
att()->set_preferred_mtu(kPreferredMTU);
110114

@@ -122,8 +126,8 @@ TEST_F(ClientTest, ExchangeMTUMalformedResponse) {
122126

123127
RunLoopUntilIdle();
124128

125-
EXPECT_EQ(ToResult(HostError::kPacketMalformed), status);
126-
EXPECT_EQ(0, final_mtu);
129+
ASSERT_TRUE(result.has_value());
130+
EXPECT_EQ(MtuResultFromHostErrCode(HostError::kPacketMalformed), *result);
127131
EXPECT_TRUE(fake_chan()->link_error());
128132
}
129133

@@ -135,12 +139,8 @@ TEST_F(ClientTest, ExchangeMTUErrorNotSupported) {
135139
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
136140
);
137141

138-
uint16_t final_mtu = 0;
139-
att::Result<> status = fitx::ok();
140-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
141-
final_mtu = val;
142-
status = cb_status;
143-
};
142+
std::optional<att::Result<uint16_t>> result;
143+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
144144

145145
// Set the initial MTU to something other than the default LE MTU since we
146146
// want to confirm that the MTU changes to the default.
@@ -154,16 +154,15 @@ TEST_F(ClientTest, ExchangeMTUErrorNotSupported) {
154154

155155
// Respond with "Request Not Supported". This will cause us to switch to the
156156
// default MTU.
157-
fake_chan()->Receive(StaticByteBuffer(0x01, // opcode: error response
158-
0x02, // request: exchange MTU
159-
0x00, 0x00, // handle: 0
160-
0x06 // error: Request Not Supported
161-
));
157+
fake_chan()->Receive(StaticByteBuffer(att::kErrorResponse, // opcode
158+
att::kExchangeMTURequest, // request opcode
159+
0x00, 0x00, // handle: 0
160+
att::ErrorCode::kRequestNotSupported));
162161

163162
RunLoopUntilIdle();
164163

165-
EXPECT_EQ(ToResult(att::ErrorCode::kRequestNotSupported), status);
166-
EXPECT_EQ(att::kLEMinMTU, final_mtu);
164+
ASSERT_TRUE(result.has_value());
165+
EXPECT_EQ(MtuResultFromErrCode(att::ErrorCode::kRequestNotSupported), *result);
167166
EXPECT_EQ(att::kLEMinMTU, att()->mtu());
168167
}
169168

@@ -174,12 +173,8 @@ TEST_F(ClientTest, ExchangeMTUErrorOther) {
174173
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
175174
);
176175

177-
uint16_t final_mtu = kPreferredMTU;
178-
att::Result<> status = fitx::ok();
179-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
180-
final_mtu = val;
181-
status = cb_status;
182-
};
176+
std::optional<att::Result<uint16_t>> result;
177+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
183178

184179
att()->set_preferred_mtu(kPreferredMTU);
185180
EXPECT_EQ(att::kLEMinMTU, att()->mtu());
@@ -190,16 +185,15 @@ TEST_F(ClientTest, ExchangeMTUErrorOther) {
190185
ASSERT_TRUE(Expect(kExpectedRequest));
191186

192187
// Respond with an error. The MTU should remain unchanged.
193-
fake_chan()->Receive(StaticByteBuffer(0x01, // opcode: error response
194-
0x02, // request: exchange MTU
195-
0x00, 0x00, // handle: 0
196-
0x0E // error: Unlikely Error
197-
));
188+
fake_chan()->Receive(StaticByteBuffer(att::kErrorResponse, // opcode
189+
att::kExchangeMTURequest, // request opcode
190+
0x00, 0x00, // handle: 0
191+
att::ErrorCode::kUnlikelyError));
198192

199193
RunLoopUntilIdle();
200194

201-
EXPECT_EQ(ToResult(att::ErrorCode::kUnlikelyError), status);
202-
EXPECT_EQ(0, final_mtu);
195+
ASSERT_TRUE(result.has_value());
196+
EXPECT_EQ(MtuResultFromErrCode(att::ErrorCode::kUnlikelyError), *result);
203197
EXPECT_EQ(att::kLEMinMTU, att()->mtu());
204198
}
205199

@@ -213,12 +207,8 @@ TEST_F(ClientTest, ExchangeMTUSelectLocal) {
213207
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
214208
);
215209

216-
uint16_t final_mtu = 0;
217-
att::Result<> status = fitx::ok();
218-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
219-
final_mtu = val;
220-
status = cb_status;
221-
};
210+
std::optional<att::Result<uint16_t>> result;
211+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
222212

223213
att()->set_preferred_mtu(kPreferredMTU);
224214

@@ -234,9 +224,8 @@ TEST_F(ClientTest, ExchangeMTUSelectLocal) {
234224
));
235225

236226
RunLoopUntilIdle();
237-
238-
EXPECT_EQ(fitx::ok(), status);
239-
EXPECT_EQ(kPreferredMTU, final_mtu);
227+
ASSERT_TRUE(result.has_value());
228+
EXPECT_EQ(att::Result<uint16_t>(fitx::ok(kPreferredMTU)), *result);
240229
EXPECT_EQ(kPreferredMTU, att()->mtu());
241230
}
242231

@@ -250,12 +239,8 @@ TEST_F(ClientTest, ExchangeMTUSelectRemote) {
250239
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
251240
);
252241

253-
uint16_t final_mtu = 0;
254-
att::Result<> status = fitx::ok();
255-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
256-
final_mtu = val;
257-
status = cb_status;
258-
};
242+
std::optional<att::Result<uint16_t>> result;
243+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
259244

260245
att()->set_preferred_mtu(kPreferredMTU);
261246

@@ -272,8 +257,8 @@ TEST_F(ClientTest, ExchangeMTUSelectRemote) {
272257

273258
RunLoopUntilIdle();
274259

275-
EXPECT_EQ(fitx::ok(), status);
276-
EXPECT_EQ(kServerRxMTU, final_mtu);
260+
ASSERT_TRUE(result.has_value());
261+
EXPECT_EQ(att::Result<uint16_t>(fitx::ok(kServerRxMTU)), *result);
277262
EXPECT_EQ(kServerRxMTU, att()->mtu());
278263
}
279264

@@ -287,12 +272,8 @@ TEST_F(ClientTest, ExchangeMTUSelectDefault) {
287272
kPreferredMTU, 0x00 // client rx mtu: kPreferredMTU
288273
);
289274

290-
uint16_t final_mtu = 0;
291-
att::Result<> status = fitx::ok();
292-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
293-
final_mtu = val;
294-
status = cb_status;
295-
};
275+
std::optional<att::Result<uint16_t>> result;
276+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
296277

297278
att()->set_preferred_mtu(kPreferredMTU);
298279

@@ -309,8 +290,8 @@ TEST_F(ClientTest, ExchangeMTUSelectDefault) {
309290

310291
RunLoopUntilIdle();
311292

312-
EXPECT_EQ(fitx::ok(), status);
313-
EXPECT_EQ(att::kLEMinMTU, final_mtu);
293+
ASSERT_TRUE(result.has_value());
294+
EXPECT_EQ(att::Result<uint16_t>(fitx::ok(att::kLEMinMTU)), *result);
314295
EXPECT_EQ(att::kLEMinMTU, att()->mtu());
315296
}
316297

@@ -3213,12 +3194,8 @@ TEST_F(ClientTest, ReadRequestSuccessNotTruncatedWhenMtuAllowsMaxValueLength) {
32133194
LowerBits(kPreferredMTU), UpperBits(kPreferredMTU) // client rx mtu
32143195
);
32153196

3216-
uint16_t final_mtu = 0;
3217-
att::Result<> mtu_status = fitx::ok();
3218-
auto mtu_cb = [&](att::Result<> cb_status, uint16_t val) {
3219-
final_mtu = val;
3220-
mtu_status = cb_status;
3221-
};
3197+
std::optional<att::Result<uint16_t>> result;
3198+
auto mtu_cb = [&](att::Result<uint16_t> cb_result) { result = cb_result; };
32223199

32233200
// Initiate the request on the loop since Expect() below blocks.
32243201
async::PostTask(dispatcher(), [this, mtu_cb] { client()->ExchangeMTU(mtu_cb); });
@@ -3232,8 +3209,8 @@ TEST_F(ClientTest, ReadRequestSuccessNotTruncatedWhenMtuAllowsMaxValueLength) {
32323209
));
32333210

32343211
RunLoopUntilIdle();
3235-
EXPECT_EQ(fitx::ok(), mtu_status);
3236-
EXPECT_EQ(kPreferredMTU, final_mtu);
3212+
ASSERT_TRUE(result.has_value());
3213+
EXPECT_EQ(att::Result<uint16_t>(fitx::ok(kPreferredMTU)), *result);
32373214
EXPECT_EQ(kPreferredMTU, att()->mtu());
32383215

32393216
constexpr att::Handle kHandle = 0x0001;
@@ -3386,6 +3363,7 @@ TEST_F(ClientTest, ReadByTypeRequestSuccess128BitUUID) {
33863363
EXPECT_TRUE(cb_called);
33873364
EXPECT_FALSE(fake_chan()->link_error());
33883365
}
3366+
33893367
TEST_F(ClientTest, ReadByTypeRequestError) {
33903368
constexpr att::Handle kStartHandle = 0x0001;
33913369
constexpr att::Handle kEndHandle = 0xFFFF;

src/connectivity/bluetooth/core/bt-host/gatt/fake_client.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ uint16_t FakeClient::mtu() const {
2323
}
2424

2525
void FakeClient::ExchangeMTU(MTUCallback callback) {
26-
auto task = [status = exchange_mtu_status_, mtu = server_mtu_, callback = std::move(callback)] {
27-
callback(status, mtu);
26+
auto task = [status = exchange_mtu_status_, mtu = server_mtu_,
27+
callback = std::move(callback)]() mutable {
28+
if (status.is_error()) {
29+
callback(fitx::error(status.error_value()));
30+
} else {
31+
callback(fitx::ok(mtu));
32+
}
2833
};
2934
async::PostTask(dispatcher_, std::move(task));
3035
}

src/connectivity/bluetooth/core/bt-host/gatt/remote_service_manager.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,20 @@ void RemoteServiceManager::Initialize(att::ResultFunction<> cb, std::vector<UUID
9696
user_init_cb(status);
9797
};
9898

99-
client_->ExchangeMTU([self, init_cb = std::move(init_cb), services = std::move(services)](
100-
att::Result<> status, uint16_t mtu) mutable {
99+
client_->ExchangeMTU([self, init_cb = std::move(init_cb),
100+
services = std::move(services)](att::Result<uint16_t> mtu_result) mutable {
101101
// The Client's Bearer may outlive this object.
102102
if (!self) {
103103
return;
104104
}
105105

106-
if (bt_is_error(status, INFO, "gatt", "MTU exchange failed")) {
107-
init_cb(status);
106+
// Support for the MTU exchange is optional, so if the peer indicated they don't support it, we
107+
// continue with initialization.
108+
if (mtu_result.is_ok() || mtu_result.error_value().is(att::ErrorCode::kRequestNotSupported)) {
109+
bt_is_error(mtu_result, DEBUG, "gatt", "MTU exchange not supported");
110+
} else {
111+
bt_log(INFO, "gatt", "MTU exchange failed: %s", bt_str(mtu_result.error_value()));
112+
init_cb(fitx::error(mtu_result.error_value()));
108113
return;
109114
}
110115

0 commit comments

Comments
 (0)