Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chromeos / dbus / cros_disks_client.cc
blobfafbdec79e8286f21e482b796792b7a082a8a665
1 // Copyright (c) 2012 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.
5 #include "chromeos/dbus/cros_disks_client.h"
7 #include <map>
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/location.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/sys_info.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/worker_pool.h"
18 #include "base/values.h"
19 #include "chromeos/dbus/fake_cros_disks_client.h"
20 #include "dbus/bus.h"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "dbus/object_proxy.h"
24 #include "dbus/values_util.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
27 namespace chromeos {
29 namespace {
31 const char* kDefaultMountOptions[] = {
32 "rw",
33 "nodev",
34 "noexec",
35 "nosuid",
38 const char* kDefaultUnmountOptions[] = {
39 "force",
42 const char kLazyUnmountOption[] = "lazy";
44 const char kMountLabelOption[] = "mountlabel";
46 // Checks if retrieved media type is in boundaries of DeviceMediaType.
47 bool IsValidMediaType(uint32 type) {
48 return type < static_cast<uint32>(cros_disks::DEVICE_MEDIA_NUM_VALUES);
51 // Translates enum used in cros-disks to enum used in Chrome.
52 // Note that we could just do static_cast, but this is less sensitive to
53 // changes in cros-disks.
54 DeviceType DeviceMediaTypeToDeviceType(uint32 media_type_uint32) {
55 if (!IsValidMediaType(media_type_uint32))
56 return DEVICE_TYPE_UNKNOWN;
58 cros_disks::DeviceMediaType media_type =
59 cros_disks::DeviceMediaType(media_type_uint32);
61 switch (media_type) {
62 case(cros_disks::DEVICE_MEDIA_UNKNOWN):
63 return DEVICE_TYPE_UNKNOWN;
64 case(cros_disks::DEVICE_MEDIA_USB):
65 return DEVICE_TYPE_USB;
66 case(cros_disks::DEVICE_MEDIA_SD):
67 return DEVICE_TYPE_SD;
68 case(cros_disks::DEVICE_MEDIA_OPTICAL_DISC):
69 return DEVICE_TYPE_OPTICAL_DISC;
70 case(cros_disks::DEVICE_MEDIA_MOBILE):
71 return DEVICE_TYPE_MOBILE;
72 case(cros_disks::DEVICE_MEDIA_DVD):
73 return DEVICE_TYPE_DVD;
74 default:
75 return DEVICE_TYPE_UNKNOWN;
79 bool ReadMountEntryFromDbus(dbus::MessageReader* reader, MountEntry* entry) {
80 uint32 error_code = 0;
81 std::string source_path;
82 uint32 mount_type = 0;
83 std::string mount_path;
84 if (!reader->PopUint32(&error_code) ||
85 !reader->PopString(&source_path) ||
86 !reader->PopUint32(&mount_type) ||
87 !reader->PopString(&mount_path)) {
88 return false;
90 *entry = MountEntry(static_cast<MountError>(error_code), source_path,
91 static_cast<MountType>(mount_type), mount_path);
92 return true;
95 // The CrosDisksClient implementation.
96 class CrosDisksClientImpl : public CrosDisksClient {
97 public:
98 CrosDisksClientImpl() : proxy_(NULL), weak_ptr_factory_(this) {}
100 // CrosDisksClient override.
101 void Mount(const std::string& source_path,
102 const std::string& source_format,
103 const std::string& mount_label,
104 const base::Closure& callback,
105 const base::Closure& error_callback) override {
106 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
107 cros_disks::kMount);
108 dbus::MessageWriter writer(&method_call);
109 writer.AppendString(source_path);
110 writer.AppendString(source_format);
111 std::vector<std::string> mount_options(kDefaultMountOptions,
112 kDefaultMountOptions +
113 arraysize(kDefaultMountOptions));
114 if (!mount_label.empty()) {
115 std::string mount_label_option = base::StringPrintf("%s=%s",
116 kMountLabelOption,
117 mount_label.c_str());
118 mount_options.push_back(mount_label_option);
120 writer.AppendArrayOfStrings(mount_options);
121 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
122 base::Bind(&CrosDisksClientImpl::OnMount,
123 weak_ptr_factory_.GetWeakPtr(),
124 callback,
125 error_callback));
128 // CrosDisksClient override.
129 void Unmount(const std::string& device_path,
130 UnmountOptions options,
131 const base::Closure& callback,
132 const base::Closure& error_callback) override {
133 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
134 cros_disks::kUnmount);
135 dbus::MessageWriter writer(&method_call);
136 writer.AppendString(device_path);
138 std::vector<std::string> unmount_options(
139 kDefaultUnmountOptions,
140 kDefaultUnmountOptions + arraysize(kDefaultUnmountOptions));
141 if (options == UNMOUNT_OPTIONS_LAZY)
142 unmount_options.push_back(kLazyUnmountOption);
144 writer.AppendArrayOfStrings(unmount_options);
145 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
146 base::Bind(&CrosDisksClientImpl::OnUnmount,
147 weak_ptr_factory_.GetWeakPtr(),
148 callback,
149 error_callback));
152 // CrosDisksClient override.
153 void EnumerateAutoMountableDevices(
154 const EnumerateAutoMountableDevicesCallback& callback,
155 const base::Closure& error_callback) override {
156 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
157 cros_disks::kEnumerateAutoMountableDevices);
158 proxy_->CallMethod(
159 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
160 base::Bind(&CrosDisksClientImpl::OnEnumerateAutoMountableDevices,
161 weak_ptr_factory_.GetWeakPtr(),
162 callback,
163 error_callback));
166 // CrosDisksClient override.
167 void EnumerateMountEntries(const EnumerateMountEntriesCallback& callback,
168 const base::Closure& error_callback) override {
169 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
170 cros_disks::kEnumerateMountEntries);
171 proxy_->CallMethod(
172 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
173 base::Bind(&CrosDisksClientImpl::OnEnumerateMountEntries,
174 weak_ptr_factory_.GetWeakPtr(),
175 callback,
176 error_callback));
179 // CrosDisksClient override.
180 void Format(const std::string& device_path,
181 const std::string& filesystem,
182 const base::Closure& callback,
183 const base::Closure& error_callback) override {
184 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
185 cros_disks::kFormat);
186 dbus::MessageWriter writer(&method_call);
187 writer.AppendString(device_path);
188 writer.AppendString(filesystem);
189 // No format option is currently specified, but we can later use this
190 // argument to specify options for the format operation.
191 std::vector<std::string> format_options;
192 writer.AppendArrayOfStrings(format_options);
193 proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
194 base::Bind(&CrosDisksClientImpl::OnFormat,
195 weak_ptr_factory_.GetWeakPtr(),
196 callback,
197 error_callback));
200 // CrosDisksClient override.
201 void GetDeviceProperties(const std::string& device_path,
202 const GetDevicePropertiesCallback& callback,
203 const base::Closure& error_callback) override {
204 dbus::MethodCall method_call(cros_disks::kCrosDisksInterface,
205 cros_disks::kGetDeviceProperties);
206 dbus::MessageWriter writer(&method_call);
207 writer.AppendString(device_path);
208 proxy_->CallMethod(&method_call,
209 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
210 base::Bind(&CrosDisksClientImpl::OnGetDeviceProperties,
211 weak_ptr_factory_.GetWeakPtr(),
212 device_path,
213 callback,
214 error_callback));
217 // CrosDisksClient override.
218 void SetMountEventHandler(
219 const MountEventHandler& mount_event_handler) override {
220 static const SignalEventTuple kSignalEventTuples[] = {
221 { cros_disks::kDeviceAdded, CROS_DISKS_DEVICE_ADDED },
222 { cros_disks::kDeviceScanned, CROS_DISKS_DEVICE_SCANNED },
223 { cros_disks::kDeviceRemoved, CROS_DISKS_DEVICE_REMOVED },
224 { cros_disks::kDiskAdded, CROS_DISKS_DISK_ADDED },
225 { cros_disks::kDiskChanged, CROS_DISKS_DISK_CHANGED },
226 { cros_disks::kDiskRemoved, CROS_DISKS_DISK_REMOVED },
228 const size_t kNumSignalEventTuples = arraysize(kSignalEventTuples);
230 for (size_t i = 0; i < kNumSignalEventTuples; ++i) {
231 proxy_->ConnectToSignal(
232 cros_disks::kCrosDisksInterface,
233 kSignalEventTuples[i].signal_name,
234 base::Bind(&CrosDisksClientImpl::OnMountEvent,
235 weak_ptr_factory_.GetWeakPtr(),
236 kSignalEventTuples[i].event_type,
237 mount_event_handler),
238 base::Bind(&CrosDisksClientImpl::OnSignalConnected,
239 weak_ptr_factory_.GetWeakPtr()));
243 // CrosDisksClient override.
244 void SetMountCompletedHandler(
245 const MountCompletedHandler& mount_completed_handler) override {
246 proxy_->ConnectToSignal(
247 cros_disks::kCrosDisksInterface,
248 cros_disks::kMountCompleted,
249 base::Bind(&CrosDisksClientImpl::OnMountCompleted,
250 weak_ptr_factory_.GetWeakPtr(),
251 mount_completed_handler),
252 base::Bind(&CrosDisksClientImpl::OnSignalConnected,
253 weak_ptr_factory_.GetWeakPtr()));
256 // CrosDisksClient override.
257 void SetFormatCompletedHandler(
258 const FormatCompletedHandler& format_completed_handler) override {
259 proxy_->ConnectToSignal(
260 cros_disks::kCrosDisksInterface,
261 cros_disks::kFormatCompleted,
262 base::Bind(&CrosDisksClientImpl::OnFormatCompleted,
263 weak_ptr_factory_.GetWeakPtr(),
264 format_completed_handler),
265 base::Bind(&CrosDisksClientImpl::OnSignalConnected,
266 weak_ptr_factory_.GetWeakPtr()));
269 protected:
270 void Init(dbus::Bus* bus) override {
271 proxy_ = bus->GetObjectProxy(
272 cros_disks::kCrosDisksServiceName,
273 dbus::ObjectPath(cros_disks::kCrosDisksServicePath));
276 private:
277 // A struct to contain a pair of signal name and mount event type.
278 // Used by SetMountEventHandler.
279 struct SignalEventTuple {
280 const char *signal_name;
281 MountEventType event_type;
284 // Handles the result of Mount and calls |callback| or |error_callback|.
285 void OnMount(const base::Closure& callback,
286 const base::Closure& error_callback,
287 dbus::Response* response) {
288 if (!response) {
289 error_callback.Run();
290 return;
292 callback.Run();
295 // Handles the result of Unmount and calls |callback| or |error_callback|.
296 void OnUnmount(const base::Closure& callback,
297 const base::Closure& error_callback,
298 dbus::Response* response) {
299 if (!response) {
300 error_callback.Run();
301 return;
304 // Temporarly allow Unmount method to report failure both by setting dbus
305 // error (in which case response is not set) and by returning mount error
306 // different from MOUNT_ERROR_NONE. This is done so we can change Unmount
307 // method to return mount error (http://crbug.com/288974) without breaking
308 // Chrome.
309 // TODO(tbarzic): When Unmount implementation is changed on cros disks side,
310 // make this fail if reader is not able to read the error code value from
311 // the response.
312 dbus::MessageReader reader(response);
313 uint32 error_code = 0;
314 if (reader.PopUint32(&error_code) &&
315 static_cast<MountError>(error_code) != MOUNT_ERROR_NONE) {
316 error_callback.Run();
317 return;
320 callback.Run();
323 // Handles the result of EnumerateAutoMountableDevices and calls |callback| or
324 // |error_callback|.
325 void OnEnumerateAutoMountableDevices(
326 const EnumerateAutoMountableDevicesCallback& callback,
327 const base::Closure& error_callback,
328 dbus::Response* response) {
329 if (!response) {
330 error_callback.Run();
331 return;
333 dbus::MessageReader reader(response);
334 std::vector<std::string> device_paths;
335 if (!reader.PopArrayOfStrings(&device_paths)) {
336 LOG(ERROR) << "Invalid response: " << response->ToString();
337 error_callback.Run();
338 return;
340 callback.Run(device_paths);
343 // Handles the result of EnumerateMountEntries and calls |callback| or
344 // |error_callback|.
345 void OnEnumerateMountEntries(
346 const EnumerateMountEntriesCallback& callback,
347 const base::Closure& error_callback,
348 dbus::Response* response) {
349 if (!response) {
350 error_callback.Run();
351 return;
354 dbus::MessageReader reader(response);
355 dbus::MessageReader array_reader(NULL);
356 if (!reader.PopArray(&array_reader)) {
357 LOG(ERROR) << "Invalid response: " << response->ToString();
358 error_callback.Run();
359 return;
362 std::vector<MountEntry> entries;
363 while (array_reader.HasMoreData()) {
364 MountEntry entry;
365 dbus::MessageReader sub_reader(NULL);
366 if (!array_reader.PopStruct(&sub_reader) ||
367 !ReadMountEntryFromDbus(&sub_reader, &entry)) {
368 LOG(ERROR) << "Invalid response: " << response->ToString();
369 error_callback.Run();
370 return;
372 entries.push_back(entry);
374 callback.Run(entries);
377 // Handles the result of Format and calls |callback| or |error_callback|.
378 void OnFormat(const base::Closure& callback,
379 const base::Closure& error_callback,
380 dbus::Response* response) {
381 if (!response) {
382 error_callback.Run();
383 return;
385 callback.Run();
388 // Handles the result of GetDeviceProperties and calls |callback| or
389 // |error_callback|.
390 void OnGetDeviceProperties(const std::string& device_path,
391 const GetDevicePropertiesCallback& callback,
392 const base::Closure& error_callback,
393 dbus::Response* response) {
394 if (!response) {
395 error_callback.Run();
396 return;
398 DiskInfo disk(device_path, response);
399 callback.Run(disk);
402 // Handles mount event signals and calls |handler|.
403 void OnMountEvent(MountEventType event_type,
404 MountEventHandler handler,
405 dbus::Signal* signal) {
406 dbus::MessageReader reader(signal);
407 std::string device;
408 if (!reader.PopString(&device)) {
409 LOG(ERROR) << "Invalid signal: " << signal->ToString();
410 return;
412 handler.Run(event_type, device);
415 // Handles MountCompleted signal and calls |handler|.
416 void OnMountCompleted(MountCompletedHandler handler, dbus::Signal* signal) {
417 dbus::MessageReader reader(signal);
418 MountEntry entry;
419 if (!ReadMountEntryFromDbus(&reader, &entry)) {
420 LOG(ERROR) << "Invalid signal: " << signal->ToString();
421 return;
423 handler.Run(entry);
426 // Handles FormatCompleted signal and calls |handler|.
427 void OnFormatCompleted(FormatCompletedHandler handler, dbus::Signal* signal) {
428 dbus::MessageReader reader(signal);
429 uint32 error_code = 0;
430 std::string device_path;
431 if (!reader.PopUint32(&error_code) || !reader.PopString(&device_path)) {
432 LOG(ERROR) << "Invalid signal: " << signal->ToString();
433 return;
435 handler.Run(static_cast<FormatError>(error_code), device_path);
438 // Handles the result of signal connection setup.
439 void OnSignalConnected(const std::string& interface,
440 const std::string& signal,
441 bool succeeded) {
442 LOG_IF(ERROR, !succeeded) << "Connect to " << interface << " " <<
443 signal << " failed.";
446 dbus::ObjectProxy* proxy_;
448 // Note: This should remain the last member so it'll be destroyed and
449 // invalidate its weak pointers before any other members are destroyed.
450 base::WeakPtrFactory<CrosDisksClientImpl> weak_ptr_factory_;
452 DISALLOW_COPY_AND_ASSIGN(CrosDisksClientImpl);
455 } // namespace
457 ////////////////////////////////////////////////////////////////////////////////
458 // DiskInfo
460 DiskInfo::DiskInfo(const std::string& device_path, dbus::Response* response)
461 : device_path_(device_path),
462 is_drive_(false),
463 has_media_(false),
464 on_boot_device_(false),
465 on_removable_device_(false),
466 device_type_(DEVICE_TYPE_UNKNOWN),
467 total_size_in_bytes_(0),
468 is_read_only_(false),
469 is_hidden_(true) {
470 InitializeFromResponse(response);
473 DiskInfo::~DiskInfo() {
476 // Initializes |this| from |response| given by the cros-disks service.
477 // Below is an example of |response|'s raw message (long string is ellipsized).
480 // message_type: MESSAGE_METHOD_RETURN
481 // destination: :1.8
482 // sender: :1.16
483 // signature: a{sv}
484 // serial: 96
485 // reply_serial: 267
487 // array [
488 // dict entry {
489 // string "DeviceFile"
490 // variant string "/dev/sdb"
491 // }
492 // dict entry {
493 // string "DeviceIsDrive"
494 // variant bool true
495 // }
496 // dict entry {
497 // string "DeviceIsMediaAvailable"
498 // variant bool true
499 // }
500 // dict entry {
501 // string "DeviceIsMounted"
502 // variant bool false
503 // }
504 // dict entry {
505 // string "DeviceIsOnBootDevice"
506 // variant bool false
507 // }
508 // dict entry {
509 // string "DeviceIsOnRemovableDevice"
510 // variant bool true
511 // }
512 // dict entry {
513 // string "DeviceIsReadOnly"
514 // variant bool false
515 // }
516 // dict entry {
517 // string "DeviceIsVirtual"
518 // variant bool false
519 // }
520 // dict entry {
521 // string "DeviceMediaType"
522 // variant uint32 1
523 // }
524 // dict entry {
525 // string "DeviceMountPaths"
526 // variant array [
527 // ]
528 // }
529 // dict entry {
530 // string "DevicePresentationHide"
531 // variant bool true
532 // }
533 // dict entry {
534 // string "DeviceSize"
535 // variant uint64 7998537728
536 // }
537 // dict entry {
538 // string "DriveIsRotational"
539 // variant bool false
540 // }
541 // dict entry {
542 // string "VendorId"
543 // variant string "18d1"
544 // }
545 // dict entry {
546 // string "VendorName"
547 // variant string "Google Inc."
548 // }
549 // dict entry {
550 // string "ProductId"
551 // variant string "4e11"
552 // }
553 // dict entry {
554 // string "ProductName"
555 // variant string "Nexus One"
556 // }
557 // dict entry {
558 // string "DriveModel"
559 // variant string "TransMemory"
560 // }
561 // dict entry {
562 // string "IdLabel"
563 // variant string ""
564 // }
565 // dict entry {
566 // string "IdUuid"
567 // variant string ""
568 // }
569 // dict entry {
570 // string "NativePath"
571 // variant string "/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/...
572 // }
573 // ]
574 void DiskInfo::InitializeFromResponse(dbus::Response* response) {
575 dbus::MessageReader reader(response);
576 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
577 base::DictionaryValue* properties = NULL;
578 if (!value || !value->GetAsDictionary(&properties))
579 return;
581 properties->GetBooleanWithoutPathExpansion(
582 cros_disks::kDeviceIsDrive, &is_drive_);
583 properties->GetBooleanWithoutPathExpansion(
584 cros_disks::kDeviceIsReadOnly, &is_read_only_);
585 properties->GetBooleanWithoutPathExpansion(
586 cros_disks::kDevicePresentationHide, &is_hidden_);
587 properties->GetBooleanWithoutPathExpansion(
588 cros_disks::kDeviceIsMediaAvailable, &has_media_);
589 properties->GetBooleanWithoutPathExpansion(
590 cros_disks::kDeviceIsOnBootDevice, &on_boot_device_);
591 properties->GetBooleanWithoutPathExpansion(
592 cros_disks::kDeviceIsOnRemovableDevice, &on_removable_device_);
593 properties->GetStringWithoutPathExpansion(
594 cros_disks::kNativePath, &system_path_);
595 properties->GetStringWithoutPathExpansion(
596 cros_disks::kDeviceFile, &file_path_);
597 properties->GetStringWithoutPathExpansion(cros_disks::kVendorId, &vendor_id_);
598 properties->GetStringWithoutPathExpansion(
599 cros_disks::kVendorName, &vendor_name_);
600 properties->GetStringWithoutPathExpansion(
601 cros_disks::kProductId, &product_id_);
602 properties->GetStringWithoutPathExpansion(
603 cros_disks::kProductName, &product_name_);
604 properties->GetStringWithoutPathExpansion(
605 cros_disks::kDriveModel, &drive_model_);
606 properties->GetStringWithoutPathExpansion(cros_disks::kIdLabel, &label_);
607 properties->GetStringWithoutPathExpansion(cros_disks::kIdUuid, &uuid_);
609 // dbus::PopDataAsValue() pops uint64 as double.
610 // The top 11 bits of uint64 are dropped by the use of double. But, this works
611 // unless the size exceeds 8 PB.
612 double device_size_double = 0;
613 if (properties->GetDoubleWithoutPathExpansion(cros_disks::kDeviceSize,
614 &device_size_double))
615 total_size_in_bytes_ = device_size_double;
617 // dbus::PopDataAsValue() pops uint32 as double.
618 double media_type_double = 0;
619 if (properties->GetDoubleWithoutPathExpansion(cros_disks::kDeviceMediaType,
620 &media_type_double))
621 device_type_ = DeviceMediaTypeToDeviceType(media_type_double);
623 base::ListValue* mount_paths = NULL;
624 if (properties->GetListWithoutPathExpansion(cros_disks::kDeviceMountPaths,
625 &mount_paths))
626 mount_paths->GetString(0, &mount_path_);
629 ////////////////////////////////////////////////////////////////////////////////
630 // CrosDisksClient
632 CrosDisksClient::CrosDisksClient() {}
634 CrosDisksClient::~CrosDisksClient() {}
636 // static
637 CrosDisksClient* CrosDisksClient::Create(DBusClientImplementationType type) {
638 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
639 return new CrosDisksClientImpl();
640 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
641 return new FakeCrosDisksClient();
644 // static
645 base::FilePath CrosDisksClient::GetArchiveMountPoint() {
646 return base::FilePath(base::SysInfo::IsRunningOnChromeOS() ?
647 FILE_PATH_LITERAL("/media/archive") :
648 FILE_PATH_LITERAL("/tmp/chromeos/media/archive"));
651 // static
652 base::FilePath CrosDisksClient::GetRemovableDiskMountPoint() {
653 return base::FilePath(base::SysInfo::IsRunningOnChromeOS() ?
654 FILE_PATH_LITERAL("/media/removable") :
655 FILE_PATH_LITERAL("/tmp/chromeos/media/removable"));
658 } // namespace chromeos