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

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

Issue 13637016: WIP: DO NOT SUBMIT (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more wip Created 7 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
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 "device/bluetooth/bluetooth_device_experimental_chromeos.h"
6
7 #include "base/bind.h"
8 #include "chromeos/dbus/dbus_thread_manager.h"
9 #include "chromeos/dbus/experimental_bluetooth_adapter_client.h"
10 #include "chromeos/dbus/experimental_bluetooth_agent_manager_client.h"
11 #include "chromeos/dbus/experimental_bluetooth_agent_service_provider.h"
12 #include "chromeos/dbus/experimental_bluetooth_device_client.h"
13 #include "dbus/bus.h"
14 #include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h"
15 #include "device/bluetooth/bluetooth_socket.h"
16 #include "third_party/cros_system_api/dbus/service_constants.h"
17
18 using device::BluetoothDevice;
19
20 namespace {
21
22 // The agent path is relativel meaningless since BlueZ only supports one
23 // at time and will fail an attempt to register another with "Already Exists"
24 // (which we fail in OnRegisterAgentError with ERROR_INPROGRESS).
25 const dbus::ObjectPath kAgentPath("/org/chromium/bluetooth_agent");
26
27 // The capability of our agents.
28 const char* kAgentCapability =
29 bluetooth_agent_manager::kKeyboardDisplayCapability;
30
31 } // namespace
32
33 namespace chromeos {
34
35 BluetoothDeviceExperimentalChromeOS::BluetoothDeviceExperimentalChromeOS(
36 BluetoothAdapterExperimentalChromeOS* adapter,
37 const dbus::ObjectPath& object_path)
38 : adapter_(adapter),
39 object_path_(object_path),
40 connecting_calls_(0),
41 weak_ptr_factory_(this) {
42 }
43
44 BluetoothDeviceExperimentalChromeOS::~BluetoothDeviceExperimentalChromeOS() {
45 }
46
47 uint32 BluetoothDeviceExperimentalChromeOS::GetBluetoothClass() const {
48 ExperimentalBluetoothDeviceClient::Properties* properties =
49 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
50 GetProperties(object_path_);
51 DCHECK(properties);
52
53 return properties->bluetooth_class.value();
54 }
55
56 std::string BluetoothDeviceExperimentalChromeOS::GetDeviceName() const {
57 ExperimentalBluetoothDeviceClient::Properties* properties =
58 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
59 GetProperties(object_path_);
60 DCHECK(properties);
61
62 return properties->alias.value();
63 }
64
65 std::string BluetoothDeviceExperimentalChromeOS::GetAddress() const {
66 ExperimentalBluetoothDeviceClient::Properties* properties =
67 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
68 GetProperties(object_path_);
69 DCHECK(properties);
70
71 return properties->address.value();
72 }
73
74 bool BluetoothDeviceExperimentalChromeOS::IsPaired() const {
75 ExperimentalBluetoothDeviceClient::Properties* properties =
76 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
77 GetProperties(object_path_);
78 DCHECK(properties);
79
80 return properties->paired.value();
81 }
82
83 bool BluetoothDeviceExperimentalChromeOS::IsConnected() const {
84 ExperimentalBluetoothDeviceClient::Properties* properties =
85 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
86 GetProperties(object_path_);
87 DCHECK(properties);
88
89 return properties->connected.value();
90 }
91
92 bool BluetoothDeviceExperimentalChromeOS::IsConnectable() const {
93 // TODO(deymo): implement
94 return false;
95 }
96
97 bool BluetoothDeviceExperimentalChromeOS::IsConnecting() const {
98 return connecting_calls_ > 0;
99 }
100
101 BluetoothDeviceExperimentalChromeOS::ServiceList
102 BluetoothDeviceExperimentalChromeOS::GetServices() const {
103 ExperimentalBluetoothDeviceClient::Properties* properties =
104 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
105 GetProperties(object_path_);
106 DCHECK(properties);
107
108 return properties->uuids.value();
109 }
110
111 void BluetoothDeviceExperimentalChromeOS::GetServiceRecords(
112 const ServiceRecordsCallback& callback,
113 const ErrorCallback& error_callback) {
114 // TODO(keybuk): not implemented; remove
115 error_callback.Run();
116 }
117
118 void BluetoothDeviceExperimentalChromeOS::ProvidesServiceWithName(
119 const std::string& name,
120 const ProvidesServiceCallback& callback) {
121 // TODO(keybuk): not implemented; remove
122 callback.Run(false);
123 }
124
125 bool BluetoothDeviceExperimentalChromeOS::ExpectingPinCode() const {
126 return !pincode_callback_.is_null();
127 }
128
129 bool BluetoothDeviceExperimentalChromeOS::ExpectingPasskey() const {
130 return !passkey_callback_.is_null();
131 }
132
133 bool BluetoothDeviceExperimentalChromeOS::ExpectingConfirmation() const {
134 return !confirmation_callback_.is_null();
135 }
136
137 void BluetoothDeviceExperimentalChromeOS::Connect(
138 BluetoothDevice::PairingDelegate* pairing_delegate,
139 const base::Closure& callback,
140 const ConnectErrorCallback& error_callback) {
141 ++connecting_calls_;
142 VLOG(1) << object_path_.value() << ": Connecting, " << connecting_calls_
143 << " in progress";
144
145 if (IsPaired() || IsConnected() || !pairing_delegate) {
146 // No need to pair, skip straight to connection.
147 ConnectInternal(callback, error_callback);
148
149 } else {
150 // Initiate high-security connection with pairing.
151 DCHECK(!pairing_delegate_);
152 DCHECK(agent_.get() == NULL);
153
154 pairing_delegate_ = pairing_delegate;
155
156 // The agent path is relatively meaningless since BlueZ only supports
157 // one per application at a time.
158 dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
159 agent_.reset(ExperimentalBluetoothAgentServiceProvider::Create(
160 system_bus, kAgentPath, this));
161 DCHECK(agent_.get() != NULL);
162
163 VLOG(1) << object_path_.value() << ": Registering agent for pairing";
164 DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()->
165 RegisterAgent(
166 kAgentPath,
167 kAgentCapability,
168 base::Bind(
169 &BluetoothDeviceExperimentalChromeOS::OnRegisterAgent,
170 weak_ptr_factory_.GetWeakPtr(),
171 callback, error_callback),
172 base::Bind(
173 &BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError,
174 weak_ptr_factory_.GetWeakPtr(),
175 error_callback));
176 }
177 }
178
179 void BluetoothDeviceExperimentalChromeOS::SetPinCode(
180 const std::string& pincode) {
181 if (!agent_.get() || pincode_callback_.is_null())
182 return;
183
184 pincode_callback_.Run(SUCCESS, pincode);
185 pincode_callback_.Reset();
186 }
187
188 void BluetoothDeviceExperimentalChromeOS::SetPasskey(uint32 passkey) {
189 if (!agent_.get() || passkey_callback_.is_null())
190 return;
191
192 passkey_callback_.Run(SUCCESS, passkey);
193 passkey_callback_.Reset();
194 }
195
196 void BluetoothDeviceExperimentalChromeOS::ConfirmPairing() {
197 if (!agent_.get() || confirmation_callback_.is_null())
198 return;
199
200 confirmation_callback_.Run(SUCCESS);
201 confirmation_callback_.Reset();
202 }
203
204 void BluetoothDeviceExperimentalChromeOS::RejectPairing() {
205 if (!agent_.get())
206 return;
207
208 if (!pincode_callback_.is_null()) {
209 pincode_callback_.Run(REJECTED, "");
210 pincode_callback_.Reset();
211 }
212 if (!passkey_callback_.is_null()) {
213 passkey_callback_.Run(REJECTED, 0);
214 passkey_callback_.Reset();
215 }
216 if (!confirmation_callback_.is_null()) {
217 confirmation_callback_.Run(REJECTED);
218 confirmation_callback_.Reset();
219 }
220 }
221
222 void BluetoothDeviceExperimentalChromeOS::CancelPairing() {
223 bool have_callback = false;
224 if (agent_.get()) {
225 if (!pincode_callback_.is_null()) {
226 pincode_callback_.Run(CANCELLED, "");
227 pincode_callback_.Reset();
228 have_callback = true;
229 }
230 if (!passkey_callback_.is_null()) {
231 passkey_callback_.Run(CANCELLED, 0);
232 passkey_callback_.Reset();
233 have_callback = true;
234 }
235 if (!confirmation_callback_.is_null()) {
236 confirmation_callback_.Run(CANCELLED);
237 confirmation_callback_.Reset();
238 have_callback = true;
239 }
240 }
241
242 // If there wasn't a callback in progress that we can reply to then we
243 // have to send a CancelPairing() to the device instead.
244 if (!have_callback) {
245 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
246 CancelPairing(
247 object_path_,
248 base::Bind(&base::DoNothing),
249 base::Bind(
250 &BluetoothDeviceExperimentalChromeOS::OnCancelPairingError,
251 weak_ptr_factory_.GetWeakPtr()));
252
253 // Unregister the agent now since we don't have a callback and if the
254 // very next command is a pairing attempt, which registers a new agent,
255 // we want that to succeed.
256 UnregisterAgent();
257 }
258 }
259
260 void BluetoothDeviceExperimentalChromeOS::Disconnect(
261 const base::Closure& callback,
262 const ErrorCallback& error_callback) {
263 VLOG(1) << object_path_.value() << ": Disconnecting";
264 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
265 Disconnect(
266 object_path_,
267 base::Bind(
268 &BluetoothDeviceExperimentalChromeOS::OnDisconnect,
269 weak_ptr_factory_.GetWeakPtr(),
270 callback),
271 base::Bind(
272 &BluetoothDeviceExperimentalChromeOS::OnDisconnectError,
273 weak_ptr_factory_.GetWeakPtr(),
274 error_callback));
275 }
276
277 void BluetoothDeviceExperimentalChromeOS::Forget(
278 const ErrorCallback& error_callback) {
279 VLOG(1) << object_path_.value() << ": Removing device";
280 DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()->
281 RemoveDevice(
282 adapter_->object_path_,
283 object_path_,
284 base::Bind(&base::DoNothing),
285 base::Bind(
286 &BluetoothDeviceExperimentalChromeOS::OnForgetError,
287 weak_ptr_factory_.GetWeakPtr(),
288 error_callback));
289 }
290
291 void BluetoothDeviceExperimentalChromeOS::ConnectToService(
292 const std::string& service_uuid,
293 const SocketCallback& callback) {
294 // TODO(keybuk): implement
295 callback.Run(scoped_refptr<device::BluetoothSocket>());
296 }
297
298 void BluetoothDeviceExperimentalChromeOS::ConnectToProfile(
299 device::BluetoothProfile* profile,
300 const ErrorCallback& error_callback) {
301 // TODO(keybuk): implement
302 error_callback.Run();
303 }
304
305 void BluetoothDeviceExperimentalChromeOS::SetOutOfBandPairingData(
306 const device::BluetoothOutOfBandPairingData& data,
307 const base::Closure& callback,
308 const ErrorCallback& error_callback) {
309 // TODO(keybuk): implement
310 error_callback.Run();
311 }
312
313 void BluetoothDeviceExperimentalChromeOS::ClearOutOfBandPairingData(
314 const base::Closure& callback,
315 const ErrorCallback& error_callback) {
316 // TODO(keybuk): implement
317 error_callback.Run();
318 }
319
320
321 void BluetoothDeviceExperimentalChromeOS::Release() {
322 DCHECK(agent_.get());
323 VLOG(1) << object_path_.value() << ": Release";
324
325 DCHECK(pairing_delegate_);
326 pairing_delegate_->DismissDisplayOrConfirm();
327
328 pincode_callback_.Reset();
329 passkey_callback_.Reset();
330 confirmation_callback_.Reset();
331
332 UnregisterAgent();
333 }
334
335 void BluetoothDeviceExperimentalChromeOS::RequestPinCode(
336 const dbus::ObjectPath& device_path,
337 const PinCodeCallback& callback) {
338 DCHECK(agent_.get());
339 DCHECK(device_path == object_path_);
340 VLOG(1) << object_path_.value() << ": RequestPinCode";
341
342 DCHECK(pairing_delegate_);
343 DCHECK(pincode_callback_.is_null());
344 pincode_callback_ = callback;
345 pairing_delegate_->RequestPinCode(this);
346 }
347
348 void BluetoothDeviceExperimentalChromeOS::DisplayPinCode(
349 const dbus::ObjectPath& device_path,
350 const std::string& pincode) {
351 DCHECK(agent_.get());
352 DCHECK(device_path == object_path_);
353 VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode;
354
355 DCHECK(pairing_delegate_);
356 pairing_delegate_->DisplayPinCode(this, pincode);
357 }
358
359 void BluetoothDeviceExperimentalChromeOS::RequestPasskey(
360 const dbus::ObjectPath& device_path,
361 const PasskeyCallback& callback) {
362 DCHECK(agent_.get());
363 DCHECK(device_path == object_path_);
364 VLOG(1) << object_path_.value() << ": RequestPasskey";
365
366 DCHECK(pairing_delegate_);
367 DCHECK(passkey_callback_.is_null());
368 passkey_callback_ = callback;
369 pairing_delegate_->RequestPasskey(this);
370 }
371
372 void BluetoothDeviceExperimentalChromeOS::DisplayPasskey(
373 const dbus::ObjectPath& device_path,
374 uint32 passkey, int16 entered) {
375 DCHECK(agent_.get());
376 DCHECK(device_path == object_path_);
377 VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey
378 << " (" << entered << " entered)";
379
380 // TODO(keybuk): disambiguate entered vs display
381
382 DCHECK(pairing_delegate_);
383 pairing_delegate_->DisplayPasskey(this, passkey);
384 }
385
386 void BluetoothDeviceExperimentalChromeOS::RequestConfirmation(
387 const dbus::ObjectPath& device_path,
388 uint32 passkey,
389 const ConfirmationCallback& callback) {
390 DCHECK(agent_.get());
391 DCHECK(device_path == object_path_);
392 VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey;
393
394 DCHECK(pairing_delegate_);
395 DCHECK(confirmation_callback_.is_null());
396 confirmation_callback_ = callback;
397 pairing_delegate_->ConfirmPasskey(this, passkey);
398 }
399
400 void BluetoothDeviceExperimentalChromeOS::RequestAuthorization(
401 const dbus::ObjectPath& device_path,
402 const ConfirmationCallback& callback) {
403 // TODO(keybuk): implement
404 callback.Run(CANCELLED);
405 }
406
407 void BluetoothDeviceExperimentalChromeOS::AuthorizeService(
408 const dbus::ObjectPath& device_path,
409 const std::string& uuid,
410 const ConfirmationCallback& callback) {
411 // TODO(keybuk): implement
412 callback.Run(CANCELLED);
413 }
414
415 void BluetoothDeviceExperimentalChromeOS::Cancel() {
416 DCHECK(agent_.get());
417 VLOG(1) << object_path_.value() << ": Cancel";
418
419 DCHECK(pairing_delegate_);
420 pairing_delegate_->DismissDisplayOrConfirm();
421 }
422
423 void BluetoothDeviceExperimentalChromeOS::ConnectInternal(
424 const base::Closure& callback,
425 const ConnectErrorCallback& error_callback) {
426 VLOG(1) << object_path_.value() << ": Connecting";
427 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
428 Connect(
429 object_path_,
430 base::Bind(
431 &BluetoothDeviceExperimentalChromeOS::OnConnect,
432 weak_ptr_factory_.GetWeakPtr(),
433 callback),
434 base::Bind(
435 &BluetoothDeviceExperimentalChromeOS::OnConnectError,
436 weak_ptr_factory_.GetWeakPtr(),
437 error_callback));
438 }
439
440 void BluetoothDeviceExperimentalChromeOS::OnConnect(
441 const base::Closure& callback) {
442 --connecting_calls_;
443 VLOG(1) << object_path_.value() << ": Connected, " << connecting_calls_
444 << " still in progress";
445
446 callback.Run();
447 }
448
449 void BluetoothDeviceExperimentalChromeOS::OnConnectError(
450 const ConnectErrorCallback& error_callback,
451 const std::string& error_name,
452 const std::string& error_message) {
453 --connecting_calls_;
454 LOG(WARNING) << object_path_.value() << ": Failed to connect device: "
455 << error_name << ": " << error_message;
456 VLOG(1) << object_path_.value() << ": " << connecting_calls_
457 << " still in progress";
458
459 // Determine the error code from error_name.
460 ConnectErrorCode error_code = ERROR_UNKNOWN;
461 if (error_name == bluetooth_adapter::kErrorFailed) {
462 error_code = ERROR_FAILED;
463 } else if (error_name == bluetooth_adapter::kErrorInProgress) {
464 error_code = ERROR_INPROGRESS;
465 } else if (error_name == bluetooth_adapter::kErrorNotSupported) {
466 error_code = ERROR_UNSUPPORTED_DEVICE;
467 }
468
469 error_callback.Run(error_code);
470 }
471
472 void BluetoothDeviceExperimentalChromeOS::OnRegisterAgent(
473 const base::Closure& callback,
474 const ConnectErrorCallback& error_callback) {
475 VLOG(1) << object_path_.value() << ": Agent registered, now pairing";
476
477 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
478 Pair(
479 object_path_,
480 base::Bind(
481 &BluetoothDeviceExperimentalChromeOS::OnPair,
482 weak_ptr_factory_.GetWeakPtr(),
483 callback, error_callback),
484 base::Bind(
485 &BluetoothDeviceExperimentalChromeOS::OnPairError,
486 weak_ptr_factory_.GetWeakPtr(),
487 error_callback));
488 }
489
490 void BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError(
491 const ConnectErrorCallback& error_callback,
492 const std::string& error_name,
493 const std::string& error_message) {
494 --connecting_calls_;
495 LOG(WARNING) << object_path_.value() << ": Failed to register agent: "
496 << error_name << ": " << error_message;
497 VLOG(1) << object_path_.value() << ": " << connecting_calls_
498 << " still in progress";
499
500 UnregisterAgent();
501
502 // Determine the error code from error_name.
503 ConnectErrorCode error_code = ERROR_UNKNOWN;
504 if (error_name == bluetooth_adapter::kErrorAlreadyExists) {
505 error_code = ERROR_INPROGRESS;
506 }
507
508 error_callback.Run(error_code);
509 }
510
511 void BluetoothDeviceExperimentalChromeOS::OnPair(
512 const base::Closure& callback,
513 const ConnectErrorCallback& error_callback) {
514 VLOG(1) << object_path_.value() << ": Paired";
515
516 // Now that we're paired, we need to set the device as trusted so that
517 // incoming connections will be accepted. This should only ever fail if
518 // the device is removed mid-pairing, so do it in the background while
519 // we connect and don't worry about errors.
520 DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()->
521 GetProperties(object_path_)->trusted.Set(
522 true,
523 base::Bind(
524 &BluetoothDeviceExperimentalChromeOS::OnSetTrusted,
525 weak_ptr_factory_.GetWeakPtr()));
526
527 UnregisterAgent();
528
529 // Now we can connect to the device!
530 ConnectInternal(callback, error_callback);
531 }
532
533 void BluetoothDeviceExperimentalChromeOS::OnPairError(
534 const ConnectErrorCallback& error_callback,
535 const std::string& error_name,
536 const std::string& error_message) {
537 --connecting_calls_;
538 LOG(WARNING) << object_path_.value() << ": Failed to connect device: "
539 << error_name << ": " << error_message;
540 VLOG(1) << object_path_.value() << ": " << connecting_calls_
541 << " still in progress";
542
543 UnregisterAgent();
544
545 // Determine the error code from error_name.
546 ConnectErrorCode error_code = ERROR_UNKNOWN;
547 if (error_name == bluetooth_adapter::kErrorConnectionAttemptFailed) {
548 error_code = ERROR_FAILED;
549 } else if (error_name == bluetooth_adapter::kErrorAuthenticationFailed) {
550 error_code = ERROR_AUTH_FAILED;
551 } else if (error_name == bluetooth_adapter::kErrorAuthenticationRejected) {
552 error_code = ERROR_AUTH_REJECTED;
553 } else if (error_name == bluetooth_adapter::kErrorAuthenticationTimeout) {
554 error_code = ERROR_AUTH_TIMEOUT;
555 }
556
557 error_callback.Run(error_code);
558 }
559
560 void BluetoothDeviceExperimentalChromeOS::OnCancelPairingError(
561 const std::string& error_name,
562 const std::string& error_message) {
563 LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: "
564 << error_name << ": " << error_message;
565 }
566
567 void BluetoothDeviceExperimentalChromeOS::OnSetTrusted(bool success) {
568 LOG(WARNING) << object_path_.value() << ": Failed to set device as trusted";
569 }
570
571 void BluetoothDeviceExperimentalChromeOS::UnregisterAgent() {
572 DCHECK(agent_.get());
573 DCHECK(pairing_delegate_);
574
575 DCHECK(pincode_callback_.is_null());
576 DCHECK(passkey_callback_.is_null());
577 DCHECK(confirmation_callback_.is_null());
578
579 pairing_delegate_ = NULL;
580 agent_.reset();
581
582 // Clean up after ourselves.
583 VLOG(1) << object_path_.value() << ": Unregistering pairing agent";
584 DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()->
585 UnregisterAgent(
586 kAgentPath,
587 base::Bind(&base::DoNothing),
588 base::Bind(
589 &BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError,
590 weak_ptr_factory_.GetWeakPtr()));
591 }
592
593 void BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError(
594 const std::string& error_name,
595 const std::string& error_message) {
596 LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: "
597 << error_name << ": " << error_message;
598 }
599
600 void BluetoothDeviceExperimentalChromeOS::OnDisconnect(
601 const base::Closure& callback) {
602 VLOG(1) << object_path_.value() << ": Disconnected";
603 callback.Run();
604 }
605
606 void BluetoothDeviceExperimentalChromeOS::OnDisconnectError(
607 const ErrorCallback& error_callback,
608 const std::string& error_name,
609 const std::string& error_message) {
610 LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: "
611 << error_name << ": " << error_message;
612 error_callback.Run();
613 }
614
615 void BluetoothDeviceExperimentalChromeOS::OnForgetError(
616 const ErrorCallback& error_callback,
617 const std::string& error_name,
618 const std::string& error_message) {
619 LOG(WARNING) << object_path_.value() << ": Failed to remove device: "
620 << error_name << ": " << error_message;
621 error_callback.Run();
622 }
623
624 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698