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"
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"
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"
31 const char* kDefaultMountOptions
[] = {
38 const char* kDefaultUnmountOptions
[] = {
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
);
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
;
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
)) {
90 *entry
= MountEntry(static_cast<MountError
>(error_code
), source_path
,
91 static_cast<MountType
>(mount_type
), mount_path
);
95 // The CrosDisksClient implementation.
96 class CrosDisksClientImpl
: public CrosDisksClient
{
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
,
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",
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(),
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(),
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
);
159 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
160 base::Bind(&CrosDisksClientImpl::OnEnumerateAutoMountableDevices
,
161 weak_ptr_factory_
.GetWeakPtr(),
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
);
172 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
173 base::Bind(&CrosDisksClientImpl::OnEnumerateMountEntries
,
174 weak_ptr_factory_
.GetWeakPtr(),
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(),
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(),
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()));
270 void Init(dbus::Bus
* bus
) override
{
271 proxy_
= bus
->GetObjectProxy(
272 cros_disks::kCrosDisksServiceName
,
273 dbus::ObjectPath(cros_disks::kCrosDisksServicePath
));
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
) {
289 error_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
) {
300 error_callback
.Run();
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
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
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();
323 // Handles the result of EnumerateAutoMountableDevices and calls |callback| or
325 void OnEnumerateAutoMountableDevices(
326 const EnumerateAutoMountableDevicesCallback
& callback
,
327 const base::Closure
& error_callback
,
328 dbus::Response
* response
) {
330 error_callback
.Run();
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();
340 callback
.Run(device_paths
);
343 // Handles the result of EnumerateMountEntries and calls |callback| or
345 void OnEnumerateMountEntries(
346 const EnumerateMountEntriesCallback
& callback
,
347 const base::Closure
& error_callback
,
348 dbus::Response
* response
) {
350 error_callback
.Run();
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();
362 std::vector
<MountEntry
> entries
;
363 while (array_reader
.HasMoreData()) {
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();
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
) {
382 error_callback
.Run();
388 // Handles the result of GetDeviceProperties and calls |callback| or
390 void OnGetDeviceProperties(const std::string
& device_path
,
391 const GetDevicePropertiesCallback
& callback
,
392 const base::Closure
& error_callback
,
393 dbus::Response
* response
) {
395 error_callback
.Run();
398 DiskInfo
disk(device_path
, response
);
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
);
408 if (!reader
.PopString(&device
)) {
409 LOG(ERROR
) << "Invalid signal: " << signal
->ToString();
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
);
419 if (!ReadMountEntryFromDbus(&reader
, &entry
)) {
420 LOG(ERROR
) << "Invalid signal: " << signal
->ToString();
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();
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
,
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
);
457 ////////////////////////////////////////////////////////////////////////////////
460 DiskInfo::DiskInfo(const std::string
& device_path
, dbus::Response
* response
)
461 : device_path_(device_path
),
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),
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
489 // string "DeviceFile"
490 // variant string "/dev/sdb"
493 // string "DeviceIsDrive"
497 // string "DeviceIsMediaAvailable"
501 // string "DeviceIsMounted"
502 // variant bool false
505 // string "DeviceIsOnBootDevice"
506 // variant bool false
509 // string "DeviceIsOnRemovableDevice"
513 // string "DeviceIsReadOnly"
514 // variant bool false
517 // string "DeviceIsVirtual"
518 // variant bool false
521 // string "DeviceMediaType"
525 // string "DeviceMountPaths"
530 // string "DevicePresentationHide"
534 // string "DeviceSize"
535 // variant uint64 7998537728
538 // string "DriveIsRotational"
539 // variant bool false
543 // variant string "18d1"
546 // string "VendorName"
547 // variant string "Google Inc."
550 // string "ProductId"
551 // variant string "4e11"
554 // string "ProductName"
555 // variant string "Nexus One"
558 // string "DriveModel"
559 // variant string "TransMemory"
570 // string "NativePath"
571 // variant string "/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/...
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
))
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
,
621 device_type_
= DeviceMediaTypeToDeviceType(media_type_double
);
623 base::ListValue
* mount_paths
= NULL
;
624 if (properties
->GetListWithoutPathExpansion(cros_disks::kDeviceMountPaths
,
626 mount_paths
->GetString(0, &mount_path_
);
629 ////////////////////////////////////////////////////////////////////////////////
632 CrosDisksClient::CrosDisksClient() {}
634 CrosDisksClient::~CrosDisksClient() {}
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();
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"));
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