blob: d9551bb29b88ce3a8ecf20f153f14b7603f62e96 [file] [log] [blame]
Zelin Liu44c16582024-09-12 09:37:311// Copyright 2024 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/bluetooth/web_bluetooth_test_utils.h"
6
7#include <memory>
8#include <optional>
9#include <string>
10#include <utility>
11
12#include "base/containers/contains.h"
13#include "base/functional/callback.h"
14#include "base/task/single_thread_task_runner.h"
15#include "chrome/browser/bluetooth/chrome_bluetooth_delegate.h"
16#include "chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.h"
17#include "chrome/browser/bluetooth/web_bluetooth_test_utils.h"
18#include "content/public/browser/render_frame_host.h"
19#include "device/bluetooth/bluetooth_adapter.h"
20#include "device/bluetooth/bluetooth_gatt_notify_session.h"
21#include "device/bluetooth/bluetooth_gatt_service.h"
22#include "device/bluetooth/bluetooth_remote_gatt_service.h"
23#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
24#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
25#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
26#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
27
28namespace {
29
30using ::device::BluetoothAdapter;
31using ::device::BluetoothGattNotifySession;
32using ::device::BluetoothGattService;
33using ::device::BluetoothRemoteGattService;
34using ::device::BluetoothUUID;
35using ::device::MockBluetoothGattCharacteristic;
36using ::device::MockBluetoothGattNotifySession;
37using ::device::MockBluetoothGattService;
38
39} // namespace
40
41FakeBluetoothAdapter::FakeBluetoothAdapter() = default;
42
43void FakeBluetoothAdapter::SetIsPresent(bool is_present) {
44 is_present_ = is_present;
45}
46
47void FakeBluetoothAdapter::SimulateDeviceAdvertisementReceived(
48 const std::string& device_address,
49 const std::optional<std::string>& advertisement_name) const {
50 for (auto& observer : observers_) {
51 observer.DeviceAdvertisementReceived(
52 device_address, /*device_name=*/std::nullopt, advertisement_name,
53 /*rssi=*/std::nullopt, /*tx_power=*/std::nullopt,
54 /*appearance=*/std::nullopt,
55 /*advertised_uuids=*/{}, /*service_data_map=*/{},
56 /*manufacturer_data_map=*/{});
57 }
58}
59
60void FakeBluetoothAdapter::AddObserver(
61 device::BluetoothAdapter::Observer* observer) {
62 device::BluetoothAdapter::AddObserver(observer);
63}
64
65bool FakeBluetoothAdapter::IsPresent() const {
66 return is_present_;
67}
68
69bool FakeBluetoothAdapter::IsPowered() const {
70 return true;
71}
72
73device::BluetoothAdapter::ConstDeviceList FakeBluetoothAdapter::GetDevices()
74 const {
75 device::BluetoothAdapter::ConstDeviceList devices;
76 for (const auto& it : mock_devices_) {
77 devices.push_back(it.get());
78 }
79 return devices;
80}
81
82device::BluetoothDevice* FakeBluetoothAdapter::GetDevice(
83 const std::string& address) {
84 for (const auto& it : mock_devices_) {
85 if (it->GetAddress() == address) {
86 return it.get();
87 }
88 }
89 return nullptr;
90}
91
92void FakeBluetoothAdapter::StartScanWithFilter(
93 std::unique_ptr<device::BluetoothDiscoveryFilter> filter,
94 base::OnceCallback<void(/*is_error*/ bool,
95 device::UMABluetoothDiscoverySessionOutcome)>
96 callback) {
97 std::move(callback).Run(
98 /*is_error=*/false, device::UMABluetoothDiscoverySessionOutcome::SUCCESS);
99}
100
101FakeBluetoothAdapter::~FakeBluetoothAdapter() = default;
102
103FakeBluetoothGattCharacteristic::FakeBluetoothGattCharacteristic(
104 MockBluetoothGattService* service,
105 const std::string& identifier,
106 const BluetoothUUID& uuid,
107 Properties properties,
108 Permissions permissions)
109 : testing::NiceMock<MockBluetoothGattCharacteristic>(service,
110 identifier,
111 uuid,
112 properties,
113 permissions),
114 value_({1}) {}
115
116FakeBluetoothGattCharacteristic::~FakeBluetoothGattCharacteristic() = default;
117
118void FakeBluetoothGattCharacteristic::ReadRemoteCharacteristic(
119 ValueCallback callback) {
120 if (!(GetProperties() & BluetoothGattCharacteristic::PROPERTY_READ)) {
121 std::move(callback).Run(BluetoothGattService::GattErrorCode::kNotPermitted,
122 std::vector<uint8_t>());
123 return;
124 }
125 if (defer_read_until_notification_start_) {
126 DCHECK(!deferred_read_callback_);
127 deferred_read_callback_ = std::move(callback);
128 return;
129 }
130 std::move(callback).Run(/*error_code=*/std::nullopt, value_);
131}
132
133void FakeBluetoothGattCharacteristic::StartNotifySession(
134 NotifySessionCallback callback,
135 ErrorCallback error_callback) {
136 if (!(GetProperties() & BluetoothGattCharacteristic::PROPERTY_NOTIFY)) {
137 std::move(error_callback)
138 .Run(BluetoothGattService::GattErrorCode::kNotPermitted);
139 return;
140 }
141 auto fake_notify_session =
142 std::make_unique<testing::NiceMock<MockBluetoothGattNotifySession>>(
143 GetWeakPtr());
144 active_notify_sessions_.insert(fake_notify_session->unique_id());
145
146 if (deferred_read_callback_) {
147 // A new value as a result of calling readValue().
148 std::move(deferred_read_callback_).Run(/*error_code=*/std::nullopt, value_);
149 }
150
151 if (emit_value_change_at_notification_start_) {
152 BluetoothAdapter* adapter = GetService()->GetDevice()->GetAdapter();
153 adapter->NotifyGattCharacteristicValueChanged(this, value_);
154
155 // NotifyGattCharacteristicValueChanged(...) posts a task to notify the
156 // renderer of the change. Do the same for |callback| to ensure
157 // StartNotifySession completes after the value change notification is
158 // received.
159 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
160 FROM_HERE,
161 base::BindOnce(std::move(callback), std::move(fake_notify_session)));
162 } else {
163 // Complete StartNotifySession normally.
164 std::move(callback).Run(std::move(fake_notify_session));
165 }
166 EXPECT_TRUE(IsNotifying());
167}
168
169void FakeBluetoothGattCharacteristic::StopNotifySession(
170 BluetoothGattNotifySession::Id session,
171 base::OnceClosure callback) {
172 EXPECT_TRUE(base::Contains(active_notify_sessions_, session));
173 std::move(callback).Run();
174}
175
176bool FakeBluetoothGattCharacteristic::IsNotifying() const {
177 return !active_notify_sessions_.empty();
178}
179
180// Do not call the readValue callback until midway through the completion
181// of the startNotification callback registration.
182// https://crbug.com/1153426
183void FakeBluetoothGattCharacteristic::DeferReadUntilNotificationStart() {
184 defer_read_until_notification_start_ = true;
185}
186
187// Possibly trigger value characteristicvaluechanged events on the page
188// during the setup of startNotifications.
189// https://crbug.com/1153426.
190void FakeBluetoothGattCharacteristic::
191 EmitChangeNotificationAtNotificationStart() {
192 emit_value_change_at_notification_start_ = true;
193}
194
195FakeBluetoothGattConnection::FakeBluetoothGattConnection(
196 scoped_refptr<device::BluetoothAdapter> adapter,
197 const std::string& device_address)
198 : testing::NiceMock<device::MockBluetoothGattConnection>(adapter,
199 device_address) {}
200
201FakeBluetoothDevice::FakeBluetoothDevice(device::MockBluetoothAdapter* adapter,
202 const std::string& address)
203 : testing::NiceMock<device::MockBluetoothDevice>(adapter,
204 /*bluetooth_class=*/0u,
205 /*name=*/"Test Device",
206 address,
207 /*paired=*/true,
208 /*connected=*/true) {}
209
210void FakeBluetoothDevice::CreateGattConnection(
211 device::BluetoothDevice::GattConnectionCallback callback,
212 std::optional<device::BluetoothUUID> service_uuid) {
213 SetConnected(true);
214 gatt_services_discovery_complete_ = true;
215 std::move(callback).Run(
216 std::make_unique<FakeBluetoothGattConnection>(adapter_, GetAddress()),
217 /*error_code=*/std::nullopt);
218}
219
220bool FakeBluetoothDevice::IsGattServicesDiscoveryComplete() const {
221 return gatt_services_discovery_complete_;
222}
223
224BluetoothRemoteGattService* FakeBluetoothDevice::GetGattService(
225 const std::string& identifier) const {
226 return GetMockService(identifier);
227}
228
229std::vector<device::BluetoothRemoteGattService*>
230FakeBluetoothDevice::GetGattServices() const {
231 return GetMockServices();
232}
233
234FakeBluetoothChooser::FakeBluetoothChooser(
235 content::BluetoothChooser::EventHandler event_handler,
236 const std::optional<std::string>& device_to_select)
237 : event_handler_(event_handler), device_to_select_(device_to_select) {}
238
239FakeBluetoothChooser::~FakeBluetoothChooser() = default;
240
241// content::BluetoothChooser implementation:
242void FakeBluetoothChooser::AddOrUpdateDevice(const std::string& device_id,
243 bool should_update_name,
244 const std::u16string& device_name,
245 bool is_gatt_connected,
246 bool is_paired,
247 int signal_strength_level) {
248 // Select the first device that is added if |device_to_select_| is not
249 // populated.
250 if (!device_to_select_) {
251 event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id);
252 return;
253 }
254
255 // Otherwise, select the added device if its device ID matches
256 // |device_to_select_|.
257 if (device_to_select_.value() == device_id) {
258 event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id);
259 }
260}
261
262TestBluetoothDelegate::TestBluetoothDelegate()
263 : ChromeBluetoothDelegate(
264 std::make_unique<ChromeBluetoothDelegateImplClient>()) {}
265
266TestBluetoothDelegate::~TestBluetoothDelegate() = default;
267
268void TestBluetoothDelegate::UseRealChooser() {
269 EXPECT_FALSE(device_to_select_.has_value());
270 use_real_chooser_ = true;
271}
272
273void TestBluetoothDelegate::SetDeviceToSelect(
274 const std::string& device_address) {
275 EXPECT_FALSE(use_real_chooser_);
276 device_to_select_ = device_address;
277}
278
279std::unique_ptr<content::BluetoothChooser>
280TestBluetoothDelegate::RunBluetoothChooser(
281 content::RenderFrameHost* frame,
282 const content::BluetoothChooser::EventHandler& event_handler) {
283 if (use_real_chooser_) {
284 return ChromeBluetoothDelegate::RunBluetoothChooser(frame, event_handler);
285 }
286 return std::make_unique<FakeBluetoothChooser>(event_handler,
287 device_to_select_);
288}
289
290std::unique_ptr<content::BluetoothScanningPrompt>
291TestBluetoothDelegate::ShowBluetoothScanningPrompt(
292 content::RenderFrameHost* frame,
293 const content::BluetoothScanningPrompt::EventHandler& event_handler) {
294 // Simulate that a prompt was accepted; no actual prompt is needed here.
295 event_handler.Run(content::BluetoothScanningPrompt::Event::kAllow);
296 return nullptr;
297}
298
299BluetoothTestContentBrowserClient::BluetoothTestContentBrowserClient() =
300 default;
301
302BluetoothTestContentBrowserClient::~BluetoothTestContentBrowserClient() =
303 default;
304
305TestBluetoothDelegate* BluetoothTestContentBrowserClient::bluetooth_delegate() {
306 return &bluetooth_delegate_;
307}
308
309content::BluetoothDelegate*
310BluetoothTestContentBrowserClient::GetBluetoothDelegate() {
311 return &bluetooth_delegate_;
312}