Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: device/bluetooth/bluetooth_remote_gatt_characteristic_win.cc

Issue 1898643002: Refactor device::BluetoothGattXXX classes to split into remote/local. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "device/bluetooth/bluetooth_remote_gatt_characteristic_win.h" 5 #include "device/bluetooth/bluetooth_remote_gatt_characteristic_win.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 51
52 if (gatt_event_handle_ != nullptr) { 52 if (gatt_event_handle_ != nullptr) {
53 task_manager_->PostUnregisterGattCharacteristicValueChangedEvent( 53 task_manager_->PostUnregisterGattCharacteristicValueChangedEvent(
54 gatt_event_handle_); 54 gatt_event_handle_);
55 gatt_event_handle_ = nullptr; 55 gatt_event_handle_ = nullptr;
56 } 56 }
57 parent_service_->GetWinAdapter()->NotifyGattCharacteristicRemoved(this); 57 parent_service_->GetWinAdapter()->NotifyGattCharacteristicRemoved(this);
58 58
59 // Clear pending StartNotifySession callbacks. 59 // Clear pending StartNotifySession callbacks.
60 for (const auto& callback : start_notify_session_callbacks_) 60 for (const auto& callback : start_notify_session_callbacks_)
61 callback.second.Run(BluetoothGattService::GATT_ERROR_FAILED); 61 callback.second.Run(BluetoothRemoteGattService::GATT_ERROR_FAILED);
62 } 62 }
63 63
64 std::string BluetoothRemoteGattCharacteristicWin::GetIdentifier() const { 64 std::string BluetoothRemoteGattCharacteristicWin::GetIdentifier() const {
65 return characteristic_identifier_; 65 return characteristic_identifier_;
66 } 66 }
67 67
68 BluetoothUUID BluetoothRemoteGattCharacteristicWin::GetUUID() const { 68 BluetoothUUID BluetoothRemoteGattCharacteristicWin::GetUUID() const {
69 return characteristic_uuid_; 69 return characteristic_uuid_;
70 } 70 }
71 71
72 bool BluetoothRemoteGattCharacteristicWin::IsLocal() const { 72 bool BluetoothRemoteGattCharacteristicWin::IsLocal() const {
73 return false; 73 return false;
74 } 74 }
75 75
76 std::vector<uint8_t>& BluetoothRemoteGattCharacteristicWin::GetValue() const { 76 std::vector<uint8_t>& BluetoothRemoteGattCharacteristicWin::GetValue() const {
77 return const_cast<std::vector<uint8_t>&>(characteristic_value_); 77 return const_cast<std::vector<uint8_t>&>(characteristic_value_);
78 } 78 }
79 79
80 BluetoothGattService* BluetoothRemoteGattCharacteristicWin::GetService() const { 80 BluetoothRemoteGattService* BluetoothRemoteGattCharacteristicWin::GetService()
81 const {
81 return parent_service_; 82 return parent_service_;
82 } 83 }
83 84
84 BluetoothGattCharacteristic::Properties 85 BluetoothRemoteGattCharacteristic::Properties
85 BluetoothRemoteGattCharacteristicWin::GetProperties() const { 86 BluetoothRemoteGattCharacteristicWin::GetProperties() const {
86 BluetoothGattCharacteristic::Properties properties = PROPERTY_NONE; 87 BluetoothRemoteGattCharacteristic::Properties properties = PROPERTY_NONE;
87 88
88 if (characteristic_info_->IsBroadcastable) 89 if (characteristic_info_->IsBroadcastable)
89 properties = properties | PROPERTY_BROADCAST; 90 properties = properties | PROPERTY_BROADCAST;
90 if (characteristic_info_->IsReadable) 91 if (characteristic_info_->IsReadable)
91 properties = properties | PROPERTY_READ; 92 properties = properties | PROPERTY_READ;
92 if (characteristic_info_->IsWritableWithoutResponse) 93 if (characteristic_info_->IsWritableWithoutResponse)
93 properties = properties | PROPERTY_WRITE_WITHOUT_RESPONSE; 94 properties = properties | PROPERTY_WRITE_WITHOUT_RESPONSE;
94 if (characteristic_info_->IsWritable) 95 if (characteristic_info_->IsWritable)
95 properties = properties | PROPERTY_WRITE; 96 properties = properties | PROPERTY_WRITE;
96 if (characteristic_info_->IsNotifiable) 97 if (characteristic_info_->IsNotifiable)
97 properties = properties | PROPERTY_NOTIFY; 98 properties = properties | PROPERTY_NOTIFY;
98 if (characteristic_info_->IsIndicatable) 99 if (characteristic_info_->IsIndicatable)
99 properties = properties | PROPERTY_INDICATE; 100 properties = properties | PROPERTY_INDICATE;
100 if (characteristic_info_->IsSignedWritable) 101 if (characteristic_info_->IsSignedWritable)
101 properties = properties | PROPERTY_AUTHENTICATED_SIGNED_WRITES; 102 properties = properties | PROPERTY_AUTHENTICATED_SIGNED_WRITES;
102 if (characteristic_info_->HasExtendedProperties) 103 if (characteristic_info_->HasExtendedProperties)
103 properties = properties | PROPERTY_EXTENDED_PROPERTIES; 104 properties = properties | PROPERTY_EXTENDED_PROPERTIES;
104 105
105 // TODO(crbug.com/589304): Information about PROPERTY_RELIABLE_WRITE and 106 // TODO(crbug.com/589304): Information about PROPERTY_RELIABLE_WRITE and
106 // PROPERTY_WRITABLE_AUXILIARIES is not available in characteristic_info_ 107 // PROPERTY_WRITABLE_AUXILIARIES is not available in characteristic_info_
107 // (BTH_LE_GATT_CHARACTERISTIC). 108 // (BTH_LE_GATT_CHARACTERISTIC).
108 109
109 return properties; 110 return properties;
110 } 111 }
111 112
112 BluetoothGattCharacteristic::Permissions 113 BluetoothRemoteGattCharacteristic::Permissions
113 BluetoothRemoteGattCharacteristicWin::GetPermissions() const { 114 BluetoothRemoteGattCharacteristicWin::GetPermissions() const {
114 BluetoothGattCharacteristic::Permissions permissions = PERMISSION_NONE; 115 BluetoothRemoteGattCharacteristic::Permissions permissions = PERMISSION_NONE;
115 116
116 if (characteristic_info_->IsReadable) 117 if (characteristic_info_->IsReadable)
117 permissions = permissions | PERMISSION_READ; 118 permissions = permissions | PERMISSION_READ;
118 if (characteristic_info_->IsWritable) 119 if (characteristic_info_->IsWritable)
119 permissions = permissions | PERMISSION_WRITE; 120 permissions = permissions | PERMISSION_WRITE;
120 121
121 return permissions; 122 return permissions;
122 } 123 }
123 124
124 bool BluetoothRemoteGattCharacteristicWin::IsNotifying() const { 125 bool BluetoothRemoteGattCharacteristicWin::IsNotifying() const {
125 return gatt_event_handle_ != nullptr; 126 return gatt_event_handle_ != nullptr;
126 } 127 }
127 128
128 std::vector<BluetoothGattDescriptor*> 129 std::vector<BluetoothRemoteGattDescriptor*>
129 BluetoothRemoteGattCharacteristicWin::GetDescriptors() const { 130 BluetoothRemoteGattCharacteristicWin::GetDescriptors() const {
130 std::vector<BluetoothGattDescriptor*> descriptors; 131 std::vector<BluetoothRemoteGattDescriptor*> descriptors;
131 for (const auto& descriptor : included_descriptors_) 132 for (const auto& descriptor : included_descriptors_)
132 descriptors.push_back(descriptor.second.get()); 133 descriptors.push_back(descriptor.second.get());
133 return descriptors; 134 return descriptors;
134 } 135 }
135 136
136 BluetoothGattDescriptor* BluetoothRemoteGattCharacteristicWin::GetDescriptor( 137 BluetoothRemoteGattDescriptor*
138 BluetoothRemoteGattCharacteristicWin::GetDescriptor(
137 const std::string& identifier) const { 139 const std::string& identifier) const {
138 GattDescriptorMap::const_iterator it = included_descriptors_.find(identifier); 140 GattDescriptorMap::const_iterator it = included_descriptors_.find(identifier);
139 if (it != included_descriptors_.end()) 141 if (it != included_descriptors_.end())
140 return it->second.get(); 142 return it->second.get();
141 return nullptr; 143 return nullptr;
142 } 144 }
143 145
144 bool BluetoothRemoteGattCharacteristicWin::AddDescriptor( 146 bool BluetoothRemoteGattCharacteristicWin::AddDescriptor(
145 BluetoothGattDescriptor* descriptor) { 147 BluetoothRemoteGattDescriptor* descriptor) {
146 NOTIMPLEMENTED(); 148 NOTIMPLEMENTED();
147 return false; 149 return false;
148 } 150 }
149 151
150 bool BluetoothRemoteGattCharacteristicWin::UpdateValue( 152 bool BluetoothRemoteGattCharacteristicWin::UpdateValue(
151 const std::vector<uint8_t>& value) { 153 const std::vector<uint8_t>& value) {
152 NOTIMPLEMENTED(); 154 NOTIMPLEMENTED();
153 return false; 155 return false;
154 } 156 }
155 157
156 void BluetoothRemoteGattCharacteristicWin::StartNotifySession( 158 void BluetoothRemoteGattCharacteristicWin::StartNotifySession(
157 const NotifySessionCallback& callback, 159 const NotifySessionCallback& callback,
158 const ErrorCallback& error_callback) { 160 const ErrorCallback& error_callback) {
159 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 161 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
160 162
161 if (IsNotifying()) { 163 if (IsNotifying()) {
162 std::unique_ptr<BluetoothGattNotifySessionWin> notify_session( 164 std::unique_ptr<BluetoothGattNotifySessionWin> notify_session(
163 new BluetoothGattNotifySessionWin(weak_ptr_factory_.GetWeakPtr())); 165 new BluetoothGattNotifySessionWin(weak_ptr_factory_.GetWeakPtr()));
164 ui_task_runner_->PostTask( 166 ui_task_runner_->PostTask(
165 FROM_HERE, 167 FROM_HERE,
166 base::Bind(callback, base::Passed(std::move(notify_session)))); 168 base::Bind(callback, base::Passed(std::move(notify_session))));
167 return; 169 return;
168 } 170 }
169 171
170 if (!characteristic_info_->IsNotifiable && 172 if (!characteristic_info_->IsNotifiable &&
171 !characteristic_info_->IsIndicatable) { 173 !characteristic_info_->IsIndicatable) {
172 ui_task_runner_->PostTask( 174 ui_task_runner_->PostTask(
173 FROM_HERE, base::Bind(error_callback, 175 FROM_HERE,
174 BluetoothGattService::GATT_ERROR_NOT_SUPPORTED)); 176 base::Bind(error_callback,
177 BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
175 return; 178 return;
176 } 179 }
177 180
178 std::vector<BluetoothGattDescriptor*> ccc_descriptors = GetDescriptorsByUUID( 181 std::vector<BluetoothRemoteGattDescriptor*> ccc_descriptors =
179 BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()); 182 GetDescriptorsByUUID(BluetoothRemoteGattDescriptor::
183 ClientCharacteristicConfigurationUuid());
180 if (ccc_descriptors.size() < 1) { 184 if (ccc_descriptors.size() < 1) {
181 ui_task_runner_->PostTask( 185 ui_task_runner_->PostTask(
182 FROM_HERE, base::Bind(error_callback, 186 FROM_HERE,
183 BluetoothGattService::GATT_ERROR_NOT_SUPPORTED)); 187 base::Bind(error_callback,
188 BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
184 return; 189 return;
185 } 190 }
186 if (ccc_descriptors.size() > 1) { 191 if (ccc_descriptors.size() > 1) {
187 ui_task_runner_->PostTask( 192 ui_task_runner_->PostTask(
188 FROM_HERE, 193 FROM_HERE, base::Bind(error_callback,
189 base::Bind(error_callback, BluetoothGattService::GATT_ERROR_FAILED)); 194 BluetoothRemoteGattService::GATT_ERROR_FAILED));
190 return; 195 return;
191 } 196 }
192 197
193 start_notify_session_callbacks_.push_back( 198 start_notify_session_callbacks_.push_back(
194 std::make_pair(callback, error_callback)); 199 std::make_pair(callback, error_callback));
195 if (gatt_event_registeration_in_progress_) 200 if (gatt_event_registeration_in_progress_)
196 return; 201 return;
197 202
198 task_manager_->PostRegisterGattCharacteristicValueChangedEvent( 203 task_manager_->PostRegisterGattCharacteristicValueChangedEvent(
199 parent_service_->GetServicePath(), characteristic_info_.get(), 204 parent_service_->GetServicePath(), characteristic_info_.get(),
200 static_cast<BluetoothRemoteGattDescriptorWin*>(ccc_descriptors[0]) 205 static_cast<BluetoothRemoteGattDescriptorWin*>(ccc_descriptors[0])
201 ->GetWinDescriptorInfo(), 206 ->GetWinDescriptorInfo(),
202 base::Bind( 207 base::Bind(
203 &BluetoothRemoteGattCharacteristicWin::GattEventRegistrationCallback, 208 &BluetoothRemoteGattCharacteristicWin::GattEventRegistrationCallback,
204 weak_ptr_factory_.GetWeakPtr()), 209 weak_ptr_factory_.GetWeakPtr()),
205 base::Bind(&BluetoothRemoteGattCharacteristicWin:: 210 base::Bind(&BluetoothRemoteGattCharacteristicWin::
206 OnGattCharacteristicValueChanged, 211 OnGattCharacteristicValueChanged,
207 weak_ptr_factory_.GetWeakPtr())); 212 weak_ptr_factory_.GetWeakPtr()));
208 gatt_event_registeration_in_progress_ = true; 213 gatt_event_registeration_in_progress_ = true;
209 } 214 }
210 215
211 void BluetoothRemoteGattCharacteristicWin::ReadRemoteCharacteristic( 216 void BluetoothRemoteGattCharacteristicWin::ReadRemoteCharacteristic(
212 const ValueCallback& callback, 217 const ValueCallback& callback,
213 const ErrorCallback& error_callback) { 218 const ErrorCallback& error_callback) {
214 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 219 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
215 220
216 if (!characteristic_info_.get()->IsReadable) { 221 if (!characteristic_info_.get()->IsReadable) {
217 error_callback.Run(BluetoothGattService::GATT_ERROR_NOT_PERMITTED); 222 error_callback.Run(BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED);
218 return; 223 return;
219 } 224 }
220 225
221 if (characteristic_value_read_or_write_in_progress_) { 226 if (characteristic_value_read_or_write_in_progress_) {
222 error_callback.Run(BluetoothGattService::GATT_ERROR_IN_PROGRESS); 227 error_callback.Run(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS);
223 return; 228 return;
224 } 229 }
225 230
226 characteristic_value_read_or_write_in_progress_ = true; 231 characteristic_value_read_or_write_in_progress_ = true;
227 read_characteristic_value_callbacks_ = 232 read_characteristic_value_callbacks_ =
228 std::make_pair(callback, error_callback); 233 std::make_pair(callback, error_callback);
229 task_manager_->PostReadGattCharacteristicValue( 234 task_manager_->PostReadGattCharacteristicValue(
230 parent_service_->GetServicePath(), characteristic_info_.get(), 235 parent_service_->GetServicePath(), characteristic_info_.get(),
231 base::Bind(&BluetoothRemoteGattCharacteristicWin:: 236 base::Bind(&BluetoothRemoteGattCharacteristicWin::
232 OnReadRemoteCharacteristicValueCallback, 237 OnReadRemoteCharacteristicValueCallback,
233 weak_ptr_factory_.GetWeakPtr())); 238 weak_ptr_factory_.GetWeakPtr()));
234 } 239 }
235 240
236 void BluetoothRemoteGattCharacteristicWin::WriteRemoteCharacteristic( 241 void BluetoothRemoteGattCharacteristicWin::WriteRemoteCharacteristic(
237 const std::vector<uint8_t>& new_value, 242 const std::vector<uint8_t>& new_value,
238 const base::Closure& callback, 243 const base::Closure& callback,
239 const ErrorCallback& error_callback) { 244 const ErrorCallback& error_callback) {
240 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 245 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
241 246
242 if (!characteristic_info_.get()->IsWritable) { 247 if (!characteristic_info_.get()->IsWritable) {
243 error_callback.Run(BluetoothGattService::GATT_ERROR_NOT_PERMITTED); 248 error_callback.Run(BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED);
244 return; 249 return;
245 } 250 }
246 251
247 if (characteristic_value_read_or_write_in_progress_) { 252 if (characteristic_value_read_or_write_in_progress_) {
248 error_callback.Run(BluetoothGattService::GATT_ERROR_IN_PROGRESS); 253 error_callback.Run(BluetoothRemoteGattService::GATT_ERROR_IN_PROGRESS);
249 return; 254 return;
250 } 255 }
251 256
252 characteristic_value_read_or_write_in_progress_ = true; 257 characteristic_value_read_or_write_in_progress_ = true;
253 write_characteristic_value_callbacks_ = 258 write_characteristic_value_callbacks_ =
254 std::make_pair(callback, error_callback); 259 std::make_pair(callback, error_callback);
255 task_manager_->PostWriteGattCharacteristicValue( 260 task_manager_->PostWriteGattCharacteristicValue(
256 parent_service_->GetServicePath(), characteristic_info_.get(), new_value, 261 parent_service_->GetServicePath(), characteristic_info_.get(), new_value,
257 base::Bind(&BluetoothRemoteGattCharacteristicWin:: 262 base::Bind(&BluetoothRemoteGattCharacteristicWin::
258 OnWriteRemoteCharacteristicValueCallback, 263 OnWriteRemoteCharacteristicValueCallback,
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 std::pair<base::Closure, ErrorCallback> callbacks; 386 std::pair<base::Closure, ErrorCallback> callbacks;
382 callbacks.swap(write_characteristic_value_callbacks_); 387 callbacks.swap(write_characteristic_value_callbacks_);
383 if (FAILED(hr)) { 388 if (FAILED(hr)) {
384 callbacks.second.Run(HRESULTToGattErrorCode(hr)); 389 callbacks.second.Run(HRESULTToGattErrorCode(hr));
385 } else { 390 } else {
386 callbacks.first.Run(); 391 callbacks.first.Run();
387 } 392 }
388 characteristic_value_read_or_write_in_progress_ = false; 393 characteristic_value_read_or_write_in_progress_ = false;
389 } 394 }
390 395
391 BluetoothGattService::GattErrorCode 396 BluetoothRemoteGattService::GattErrorCode
392 BluetoothRemoteGattCharacteristicWin::HRESULTToGattErrorCode(HRESULT hr) { 397 BluetoothRemoteGattCharacteristicWin::HRESULTToGattErrorCode(HRESULT hr) {
393 if (HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER) == hr) 398 if (HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER) == hr)
394 return BluetoothGattService::GATT_ERROR_INVALID_LENGTH; 399 return BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH;
395 400
396 switch (hr) { 401 switch (hr) {
397 case E_BLUETOOTH_ATT_READ_NOT_PERMITTED: 402 case E_BLUETOOTH_ATT_READ_NOT_PERMITTED:
398 case E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED: 403 case E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED:
399 return BluetoothGattService::GATT_ERROR_NOT_PERMITTED; 404 return BluetoothRemoteGattService::GATT_ERROR_NOT_PERMITTED;
400 case E_BLUETOOTH_ATT_UNKNOWN_ERROR: 405 case E_BLUETOOTH_ATT_UNKNOWN_ERROR:
401 return BluetoothGattService::GATT_ERROR_UNKNOWN; 406 return BluetoothRemoteGattService::GATT_ERROR_UNKNOWN;
402 case E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH: 407 case E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH:
403 return BluetoothGattService::GATT_ERROR_INVALID_LENGTH; 408 return BluetoothRemoteGattService::GATT_ERROR_INVALID_LENGTH;
404 case E_BLUETOOTH_ATT_REQUEST_NOT_SUPPORTED: 409 case E_BLUETOOTH_ATT_REQUEST_NOT_SUPPORTED:
405 return BluetoothGattService::GATT_ERROR_NOT_SUPPORTED; 410 return BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED;
406 default: 411 default:
407 return BluetoothGattService::GATT_ERROR_FAILED; 412 return BluetoothRemoteGattService::GATT_ERROR_FAILED;
408 } 413 }
409 } 414 }
410 415
411 void BluetoothRemoteGattCharacteristicWin::OnGattCharacteristicValueChanged( 416 void BluetoothRemoteGattCharacteristicWin::OnGattCharacteristicValueChanged(
412 std::unique_ptr<std::vector<uint8_t>> new_value) { 417 std::unique_ptr<std::vector<uint8_t>> new_value) {
413 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 418 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
414 419
415 characteristic_value_.assign(new_value->begin(), new_value->end()); 420 characteristic_value_.assign(new_value->begin(), new_value->end());
416 parent_service_->GetWinAdapter()->NotifyGattCharacteristicValueChanged( 421 parent_service_->GetWinAdapter()->NotifyGattCharacteristicValueChanged(
417 this, characteristic_value_); 422 this, characteristic_value_);
(...skipping 21 matching lines...) Expand all
439 444
440 void BluetoothRemoteGattCharacteristicWin::ClearIncludedDescriptors() { 445 void BluetoothRemoteGattCharacteristicWin::ClearIncludedDescriptors() {
441 // Explicitly reset to null to ensure that calling GetDescriptor() on the 446 // Explicitly reset to null to ensure that calling GetDescriptor() on the
442 // removed descriptor in GattDescriptorRemoved() returns null. 447 // removed descriptor in GattDescriptorRemoved() returns null.
443 for (auto& entry : included_descriptors_) 448 for (auto& entry : included_descriptors_)
444 entry.second.reset(); 449 entry.second.reset();
445 included_descriptors_.clear(); 450 included_descriptors_.clear();
446 } 451 }
447 452
448 } // namespace device. 453 } // namespace device.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698