OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/bluetooth/bluetooth_device_chooser_controller.h" | 5 #include "content/browser/bluetooth/bluetooth_device_chooser_controller.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 #include <unordered_set> | 9 #include <unordered_set> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
15 #include "content/browser/bluetooth/bluetooth_blacklist.h" | 15 #include "content/browser/bluetooth/bluetooth_blacklist.h" |
16 #include "content/browser/bluetooth/bluetooth_metrics.h" | 16 #include "content/browser/bluetooth/bluetooth_metrics.h" |
17 #include "content/browser/bluetooth/first_device_bluetooth_chooser.h" | 17 #include "content/browser/bluetooth/first_device_bluetooth_chooser.h" |
18 #include "content/browser/bluetooth/web_bluetooth_service_impl.h" | 18 #include "content/browser/bluetooth/web_bluetooth_service_impl.h" |
19 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
20 #include "content/public/browser/content_browser_client.h" | 20 #include "content/public/browser/content_browser_client.h" |
21 #include "content/public/browser/render_frame_host.h" | 21 #include "content/public/browser/render_frame_host.h" |
22 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
23 #include "content/public/browser/web_contents_delegate.h" | 23 #include "content/public/browser/web_contents_delegate.h" |
24 #include "device/bluetooth/bluetooth_adapter.h" | 24 #include "device/bluetooth/bluetooth_adapter.h" |
25 #include "device/bluetooth/bluetooth_discovery_session.h" | 25 #include "device/bluetooth/bluetooth_discovery_session.h" |
26 | 26 |
| 27 using device::BluetoothUUID; |
| 28 |
27 namespace content { | 29 namespace content { |
28 | 30 |
29 namespace { | 31 namespace { |
30 constexpr size_t kMaxLengthForDeviceName = | 32 constexpr size_t kMaxLengthForDeviceName = |
31 29; // max length of device name in filter. | 33 29; // max length of device name in filter. |
32 | 34 |
33 void LogRequestDeviceOptions( | 35 void LogRequestDeviceOptions( |
34 const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) { | 36 const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) { |
35 VLOG(1) << "requestDevice called with the following filters: "; | 37 VLOG(1) << "requestDevice called with the following filters: "; |
36 int i = 0; | 38 int i = 0; |
37 for (const auto& filter : options->filters) { | 39 for (const auto& filter : options->filters) { |
38 VLOG(1) << "Filter #" << ++i; | 40 VLOG(1) << "Filter #" << ++i; |
39 if (!filter->name.is_null()) | 41 if (!filter->name.is_null()) |
40 VLOG(1) << "Name: " << filter->name; | 42 VLOG(1) << "Name: " << filter->name; |
41 | 43 |
42 if (!filter->name_prefix.is_null()) | 44 if (!filter->name_prefix.is_null()) |
43 VLOG(1) << "Name Prefix: " << filter->name_prefix; | 45 VLOG(1) << "Name Prefix: " << filter->name_prefix; |
44 | 46 |
45 if (!filter->services.is_null()) { | 47 if (!filter->services.is_null()) { |
46 VLOG(1) << "Services: "; | 48 VLOG(1) << "Services: "; |
47 VLOG(1) << "\t["; | 49 VLOG(1) << "\t["; |
48 for (const auto& service : filter->services) | 50 for (const auto& service : filter->services) |
49 VLOG(1) << "\t\t" << service; | 51 VLOG(1) << "\t\t" << service->canonical_value(); |
50 VLOG(1) << "\t]"; | 52 VLOG(1) << "\t]"; |
51 } | 53 } |
52 } | 54 } |
53 } | 55 } |
54 | 56 |
55 bool IsValidUUID(const mojo::String& uuid) { | |
56 device::BluetoothUUID parsed_uuid(uuid); | |
57 return parsed_uuid.IsValid() && | |
58 parsed_uuid.format() == device::BluetoothUUID::kFormat128Bit; | |
59 } | |
60 | |
61 bool HasInvalidOptionalServices( | |
62 const mojo::Array<mojo::String>& optional_services) { | |
63 return optional_services.end() != std::find_if_not(optional_services.begin(), | |
64 optional_services.end(), | |
65 IsValidUUID); | |
66 } | |
67 | |
68 bool IsEmptyOrInvalidFilter( | 57 bool IsEmptyOrInvalidFilter( |
69 const blink::mojom::WebBluetoothScanFilterPtr& filter) { | 58 const blink::mojom::WebBluetoothScanFilterPtr& filter) { |
70 // At least one member needs to be present. | 59 // At least one member needs to be present. |
71 if (filter->name.is_null() && filter->name_prefix.is_null() && | 60 if (filter->name.is_null() && filter->name_prefix.is_null() && |
72 filter->services.is_null()) | 61 filter->services.is_null()) |
73 return true; | 62 return true; |
74 | 63 |
75 // The renderer will never send a name or a name_prefix longer than | 64 // The renderer will never send a name or a name_prefix longer than |
76 // kMaxLengthForDeviceName. | 65 // kMaxLengthForDeviceName. |
77 if (!filter->name.is_null() && filter->name.size() > kMaxLengthForDeviceName) | 66 if (!filter->name.is_null() && filter->name.size() > kMaxLengthForDeviceName) |
78 return true; | 67 return true; |
79 if (!filter->name_prefix.is_null() && | 68 if (!filter->name_prefix.is_null() && |
80 filter->name_prefix.size() > kMaxLengthForDeviceName) | 69 filter->name_prefix.size() > kMaxLengthForDeviceName) |
81 return true; | 70 return true; |
82 | 71 |
83 if (!filter->services.is_null()) { | |
84 const auto& services = filter->services; | |
85 return services.end() != | |
86 std::find_if_not(services.begin(), services.end(), IsValidUUID); | |
87 } | |
88 | |
89 return false; | 72 return false; |
90 } | 73 } |
91 | 74 |
92 bool HasEmptyOrInvalidFilter( | 75 bool HasEmptyOrInvalidFilter( |
93 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { | 76 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { |
94 return filters.empty() | 77 return filters.empty() |
95 ? true | 78 ? true |
96 : filters.end() != std::find_if(filters.begin(), filters.end(), | 79 : filters.end() != std::find_if(filters.begin(), filters.end(), |
97 IsEmptyOrInvalidFilter); | 80 IsEmptyOrInvalidFilter); |
98 } | 81 } |
99 | 82 |
100 bool MatchesFilter(const device::BluetoothDevice& device, | 83 bool MatchesFilter(const device::BluetoothDevice& device, |
101 const blink::mojom::WebBluetoothScanFilterPtr& filter) { | 84 const blink::mojom::WebBluetoothScanFilterPtr& filter) { |
102 DCHECK(!IsEmptyOrInvalidFilter(filter)); | 85 DCHECK(!IsEmptyOrInvalidFilter(filter)); |
103 | 86 |
104 const std::string device_name = base::UTF16ToUTF8(device.GetName()); | 87 const std::string device_name = base::UTF16ToUTF8(device.GetName()); |
105 | 88 |
106 if (!filter->name.is_null() && (device_name != filter->name)) { | 89 if (!filter->name.is_null() && (device_name != filter->name)) { |
107 return false; | 90 return false; |
108 } | 91 } |
109 | 92 |
110 if (!filter->name_prefix.is_null() && | 93 if (!filter->name_prefix.is_null() && |
111 (!base::StartsWith(device_name, filter->name_prefix.get(), | 94 (!base::StartsWith(device_name, filter->name_prefix.get(), |
112 base::CompareCase::SENSITIVE))) { | 95 base::CompareCase::SENSITIVE))) { |
113 return false; | 96 return false; |
114 } | 97 } |
115 | 98 |
116 if (!filter->services.is_null()) { | 99 if (!filter->services.is_null()) { |
117 const auto& device_uuid_list = device.GetUUIDs(); | 100 const auto& device_uuid_list = device.GetUUIDs(); |
118 const std::set<device::BluetoothUUID> device_uuids(device_uuid_list.begin(), | 101 const std::set<BluetoothUUID> device_uuids(device_uuid_list.begin(), |
119 device_uuid_list.end()); | 102 device_uuid_list.end()); |
120 for (const auto& service : filter->services) { | 103 for (const std::unique_ptr<BluetoothUUID>& service : filter->services) { |
121 if (!ContainsKey(device_uuids, device::BluetoothUUID(service))) { | 104 if (!ContainsKey(device_uuids, *service)) { |
122 return false; | 105 return false; |
123 } | 106 } |
124 } | 107 } |
125 } | 108 } |
126 | 109 |
127 return true; | 110 return true; |
128 } | 111 } |
129 | 112 |
130 bool MatchesFilters( | 113 bool MatchesFilters( |
131 const device::BluetoothDevice& device, | 114 const device::BluetoothDevice& device, |
132 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { | 115 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { |
133 DCHECK(!HasEmptyOrInvalidFilter(filters)); | 116 DCHECK(!HasEmptyOrInvalidFilter(filters)); |
134 for (const auto& filter : filters) { | 117 for (const auto& filter : filters) { |
135 if (MatchesFilter(device, filter)) { | 118 if (MatchesFilter(device, filter)) { |
136 return true; | 119 return true; |
137 } | 120 } |
138 } | 121 } |
139 return false; | 122 return false; |
140 } | 123 } |
141 | 124 |
142 std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter( | 125 std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter( |
143 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { | 126 const mojo::Array<blink::mojom::WebBluetoothScanFilterPtr>& filters) { |
144 std::unordered_set<std::string> services; | 127 std::set<BluetoothUUID> services; |
145 for (const auto& filter : filters) { | 128 for (const auto& filter : filters) { |
146 for (const std::string& service : filter->services) { | 129 for (const std::unique_ptr<BluetoothUUID>& service : filter->services) { |
147 services.insert(service); | 130 services.insert(*service); |
148 } | 131 } |
149 } | 132 } |
150 auto discovery_filter = base::MakeUnique<device::BluetoothDiscoveryFilter>( | 133 auto discovery_filter = base::MakeUnique<device::BluetoothDiscoveryFilter>( |
151 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL); | 134 device::BluetoothDiscoveryFilter::TRANSPORT_DUAL); |
152 for (const std::string& service : services) { | 135 for (const BluetoothUUID& service : services) { |
153 discovery_filter->AddUUID(device::BluetoothUUID(service)); | 136 discovery_filter->AddUUID(service); |
154 } | 137 } |
155 return discovery_filter; | 138 return discovery_filter; |
156 } | 139 } |
157 | 140 |
158 void StopDiscoverySession( | 141 void StopDiscoverySession( |
159 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) { | 142 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) { |
160 // Nothing goes wrong if the discovery session fails to stop, and we don't | 143 // Nothing goes wrong if the discovery session fails to stop, and we don't |
161 // need to wait for it before letting the user's script proceed, so we ignore | 144 // need to wait for it before letting the user's script proceed, so we ignore |
162 // the results here. | 145 // the results here. |
163 discovery_session->Stop(base::Bind(&base::DoNothing), | 146 discovery_session->Stop(base::Bind(&base::DoNothing), |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 209 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
227 | 210 |
228 // GetDevice should only be called once. | 211 // GetDevice should only be called once. |
229 DCHECK(success_callback_.is_null()); | 212 DCHECK(success_callback_.is_null()); |
230 DCHECK(error_callback_.is_null()); | 213 DCHECK(error_callback_.is_null()); |
231 | 214 |
232 success_callback_ = success_callback; | 215 success_callback_ = success_callback; |
233 error_callback_ = error_callback; | 216 error_callback_ = error_callback; |
234 | 217 |
235 // The renderer should never send empty filters. | 218 // The renderer should never send empty filters. |
236 if (HasEmptyOrInvalidFilter(options->filters) || | 219 if (HasEmptyOrInvalidFilter(options->filters)) { |
237 HasInvalidOptionalServices(options->optional_services)) { | |
238 web_bluetooth_service_->CrashRendererAndClosePipe( | 220 web_bluetooth_service_->CrashRendererAndClosePipe( |
239 bad_message::BDH_EMPTY_OR_INVALID_FILTERS); | 221 bad_message::BDH_EMPTY_OR_INVALID_FILTERS); |
240 return; | 222 return; |
241 } | 223 } |
242 options_ = std::move(options); | 224 options_ = std::move(options); |
243 LogRequestDeviceOptions(options_); | 225 LogRequestDeviceOptions(options_); |
244 | 226 |
245 // Check blacklist to reject invalid filters and adjust optional_services. | 227 // Check blacklist to reject invalid filters and adjust optional_services. |
246 if (BluetoothBlacklist::Get().IsExcluded(options_->filters)) { | 228 if (BluetoothBlacklist::Get().IsExcluded(options_->filters)) { |
247 RecordRequestDeviceOutcome( | 229 RecordRequestDeviceOutcome( |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 | 456 |
475 void BluetoothDeviceChooserController::PostErrorCallback( | 457 void BluetoothDeviceChooserController::PostErrorCallback( |
476 blink::mojom::WebBluetoothError error) { | 458 blink::mojom::WebBluetoothError error) { |
477 if (!base::ThreadTaskRunnerHandle::Get()->PostTask( | 459 if (!base::ThreadTaskRunnerHandle::Get()->PostTask( |
478 FROM_HERE, base::Bind(error_callback_, error))) { | 460 FROM_HERE, base::Bind(error_callback_, error))) { |
479 LOG(WARNING) << "No TaskRunner."; | 461 LOG(WARNING) << "No TaskRunner."; |
480 } | 462 } |
481 } | 463 } |
482 | 464 |
483 } // namespace content | 465 } // namespace content |
OLD | NEW |