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/message_loop/message_loop_proxy.h"
14 #include "base/stl_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/sys_info.h"
17 #include "base/task_runner_util.h"
18 #include "base/threading/worker_pool.h"
19 #include "base/values.h"
20 #include "chromeos/dbus/fake_cros_disks_client.h"
22 #include "dbus/message.h"
23 #include "dbus/object_path.h"
24 #include "dbus/object_proxy.h"
25 #include "dbus/values_util.h"
26 #include "third_party/cros_system_api/dbus/service_constants.h"
32 const char* kDefaultMountOptions
[] = {
39 const char* kDefaultUnmountOptions
[] = {
43 const char kLazyUnmountOption
[] = "lazy";
45 const char kMountLabelOption
[] = "mountlabel";
47 // Checks if retrieved media type is in boundaries of DeviceMediaType.
48 bool IsValidMediaType(uint32 type
) {
49 return type
< static_cast<uint32
>(cros_disks::DEVICE_MEDIA_NUM_VALUES
);
52 // Translates enum used in cros-disks to enum used in Chrome.
53 // Note that we could just do static_cast, but this is less sensitive to
54 // changes in cros-disks.
55 DeviceType
DeviceMediaTypeToDeviceType(uint32 media_type_uint32
) {
56 if (!IsValidMediaType(media_type_uint32
))
57 return DEVICE_TYPE_UNKNOWN
;
59 cros_disks::DeviceMediaType media_type
=
60 cros_disks::DeviceMediaType(media_type_uint32
);
63 case(cros_disks::DEVICE_MEDIA_UNKNOWN
):
64 return DEVICE_TYPE_UNKNOWN
;
65 case(cros_disks::DEVICE_MEDIA_USB
):
66 return DEVICE_TYPE_USB
;
67 case(cros_disks::DEVICE_MEDIA_SD
):
68 return DEVICE_TYPE_SD
;
69 case(cros_disks::DEVICE_MEDIA_OPTICAL_DISC
):
70 return DEVICE_TYPE_OPTICAL_DISC
;
71 case(cros_disks::DEVICE_MEDIA_MOBILE
):
72 return DEVICE_TYPE_MOBILE
;
73 case(cros_disks::DEVICE_MEDIA_DVD
):
74 return DEVICE_TYPE_DVD
;
76 return DEVICE_TYPE_UNKNOWN
;
80 bool ReadMountEntryFromDbus(dbus::MessageReader
* reader
, MountEntry
* entry
) {
81 uint32 error_code
= 0;
82 std::string source_path
;
83 uint32 mount_type
= 0;
84 std::string mount_path
;
85 if (!reader
->PopUint32(&error_code
) ||
86 !reader
->PopString(&source_path
) ||
87 !reader
->PopUint32(&mount_type
) ||
88 !reader
->PopString(&mount_path
)) {
91 *entry
= MountEntry(static_cast<MountError
>(error_code
), source_path
,
92 static_cast<MountType
>(mount_type
), mount_path
);
96 // The CrosDisksClient implementation.
97 class CrosDisksClientImpl
: public CrosDisksClient
{
99 CrosDisksClientImpl() : proxy_(NULL
), weak_ptr_factory_(this) {}
101 // CrosDisksClient override.
102 void Mount(const std::string
& source_path
,
103 const std::string
& source_format
,
104 const std::string
& mount_label
,
105 const base::Closure
& callback
,
106 const base::Closure
& error_callback
) override
{
107 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
109 dbus::MessageWriter
writer(&method_call
);
110 writer
.AppendString(source_path
);
111 writer
.AppendString(source_format
);
112 std::vector
<std::string
> mount_options(kDefaultMountOptions
,
113 kDefaultMountOptions
+
114 arraysize(kDefaultMountOptions
));
115 if (!mount_label
.empty()) {
116 std::string mount_label_option
= base::StringPrintf("%s=%s",
118 mount_label
.c_str());
119 mount_options
.push_back(mount_label_option
);
121 writer
.AppendArrayOfStrings(mount_options
);
122 proxy_
->CallMethod(&method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
123 base::Bind(&CrosDisksClientImpl::OnMount
,
124 weak_ptr_factory_
.GetWeakPtr(),
129 // CrosDisksClient override.
130 void Unmount(const std::string
& device_path
,
131 UnmountOptions options
,
132 const base::Closure
& callback
,
133 const base::Closure
& error_callback
) override
{
134 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
135 cros_disks::kUnmount
);
136 dbus::MessageWriter
writer(&method_call
);
137 writer
.AppendString(device_path
);
139 std::vector
<std::string
> unmount_options(
140 kDefaultUnmountOptions
,
141 kDefaultUnmountOptions
+ arraysize(kDefaultUnmountOptions
));
142 if (options
== UNMOUNT_OPTIONS_LAZY
)
143 unmount_options
.push_back(kLazyUnmountOption
);
145 writer
.AppendArrayOfStrings(unmount_options
);
146 proxy_
->CallMethod(&method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
147 base::Bind(&CrosDisksClientImpl::OnUnmount
,
148 weak_ptr_factory_
.GetWeakPtr(),
153 // CrosDisksClient override.
154 void EnumerateAutoMountableDevices(
155 const EnumerateAutoMountableDevicesCallback
& callback
,
156 const base::Closure
& error_callback
) override
{
157 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
158 cros_disks::kEnumerateAutoMountableDevices
);
160 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
161 base::Bind(&CrosDisksClientImpl::OnEnumerateAutoMountableDevices
,
162 weak_ptr_factory_
.GetWeakPtr(),
167 // CrosDisksClient override.
168 void EnumerateMountEntries(const EnumerateMountEntriesCallback
& callback
,
169 const base::Closure
& error_callback
) override
{
170 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
171 cros_disks::kEnumerateMountEntries
);
173 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
174 base::Bind(&CrosDisksClientImpl::OnEnumerateMountEntries
,
175 weak_ptr_factory_
.GetWeakPtr(),
180 // CrosDisksClient override.
181 void Format(const std::string
& device_path
,
182 const std::string
& filesystem
,
183 const base::Closure
& callback
,
184 const base::Closure
& error_callback
) override
{
185 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
186 cros_disks::kFormat
);
187 dbus::MessageWriter
writer(&method_call
);
188 writer
.AppendString(device_path
);
189 writer
.AppendString(filesystem
);
190 // No format option is currently specified, but we can later use this
191 // argument to specify options for the format operation.
192 std::vector
<std::string
> format_options
;
193 writer
.AppendArrayOfStrings(format_options
);
194 proxy_
->CallMethod(&method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
195 base::Bind(&CrosDisksClientImpl::OnFormat
,
196 weak_ptr_factory_
.GetWeakPtr(),
201 // CrosDisksClient override.
202 void GetDeviceProperties(const std::string
& device_path
,
203 const GetDevicePropertiesCallback
& callback
,
204 const base::Closure
& error_callback
) override
{
205 dbus::MethodCall
method_call(cros_disks::kCrosDisksInterface
,
206 cros_disks::kGetDeviceProperties
);
207 dbus::MessageWriter
writer(&method_call
);
208 writer
.AppendString(device_path
);
209 proxy_
->CallMethod(&method_call
,
210 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
211 base::Bind(&CrosDisksClientImpl::OnGetDeviceProperties
,
212 weak_ptr_factory_
.GetWeakPtr(),
218 // CrosDisksClient override.
219 void SetMountEventHandler(
220 const MountEventHandler
& mount_event_handler
) override
{
221 static const SignalEventTuple kSignalEventTuples
[] = {
222 { cros_disks::kDeviceAdded
, CROS_DISKS_DEVICE_ADDED
},
223 { cros_disks::kDeviceScanned
, CROS_DISKS_DEVICE_SCANNED
},
224 { cros_disks::kDeviceRemoved
, CROS_DISKS_DEVICE_REMOVED
},
225 { cros_disks::kDiskAdded
, CROS_DISKS_DISK_ADDED
},
226 { cros_disks::kDiskChanged
, CROS_DISKS_DISK_CHANGED
},
227 { cros_disks::kDiskRemoved
, CROS_DISKS_DISK_REMOVED
},
229 const size_t kNumSignalEventTuples
= arraysize(kSignalEventTuples
);
231 for (size_t i
= 0; i
< kNumSignalEventTuples
; ++i
) {
232 proxy_
->ConnectToSignal(
233 cros_disks::kCrosDisksInterface
,
234 kSignalEventTuples
[i
].signal_name
,
235 base::Bind(&CrosDisksClientImpl::OnMountEvent
,
236 weak_ptr_factory_
.GetWeakPtr(),
237 kSignalEventTuples
[i
].event_type
,
238 mount_event_handler
),
239 base::Bind(&CrosDisksClientImpl::OnSignalConnected
,
240 weak_ptr_factory_
.GetWeakPtr()));
244 // CrosDisksClient override.
245 void SetMountCompletedHandler(
246 const MountCompletedHandler
& mount_completed_handler
) override
{
247 proxy_
->ConnectToSignal(
248 cros_disks::kCrosDisksInterface
,
249 cros_disks::kMountCompleted
,
250 base::Bind(&CrosDisksClientImpl::OnMountCompleted
,
251 weak_ptr_factory_
.GetWeakPtr(),
252 mount_completed_handler
),
253 base::Bind(&CrosDisksClientImpl::OnSignalConnected
,
254 weak_ptr_factory_
.GetWeakPtr()));
257 // CrosDisksClient override.
258 void SetFormatCompletedHandler(
259 const FormatCompletedHandler
& format_completed_handler
) override
{
260 proxy_
->ConnectToSignal(
261 cros_disks::kCrosDisksInterface
,
262 cros_disks::kFormatCompleted
,
263 base::Bind(&CrosDisksClientImpl::OnFormatCompleted
,
264 weak_ptr_factory_
.GetWeakPtr(),
265 format_completed_handler
),
266 base::Bind(&CrosDisksClientImpl::OnSignalConnected
,
267 weak_ptr_factory_
.GetWeakPtr()));
271 void Init(dbus::Bus
* bus
) override
{
272 proxy_
= bus
->GetObjectProxy(
273 cros_disks::kCrosDisksServiceName
,
274 dbus::ObjectPath(cros_disks::kCrosDisksServicePath
));
278 // A struct to contain a pair of signal name and mount event type.
279 // Used by SetMountEventHandler.
280 struct SignalEventTuple
{
281 const char *signal_name
;
282 MountEventType event_type
;
285 // Handles the result of Mount and calls |callback| or |error_callback|.
286 void OnMount(const base::Closure
& callback
,
287 const base::Closure
& error_callback
,
288 dbus::Response
* response
) {
290 error_callback
.Run();
296 // Handles the result of Unmount and calls |callback| or |error_callback|.
297 void OnUnmount(const base::Closure
& callback
,
298 const base::Closure
& error_callback
,
299 dbus::Response
* response
) {
301 error_callback
.Run();
305 // Temporarly allow Unmount method to report failure both by setting dbus
306 // error (in which case response is not set) and by returning mount error
307 // different from MOUNT_ERROR_NONE. This is done so we can change Unmount
308 // method to return mount error (http://crbug.com/288974) without breaking
310 // TODO(tbarzic): When Unmount implementation is changed on cros disks side,
311 // make this fail if reader is not able to read the error code value from
313 dbus::MessageReader
reader(response
);
314 uint32 error_code
= 0;
315 if (reader
.PopUint32(&error_code
) &&
316 static_cast<MountError
>(error_code
) != MOUNT_ERROR_NONE
) {
317 error_callback
.Run();
324 // Handles the result of EnumerateAutoMountableDevices and calls |callback| or
326 void OnEnumerateAutoMountableDevices(
327 const EnumerateAutoMountableDevicesCallback
& callback
,
328 const base::Closure
& error_callback
,
329 dbus::Response
* response
) {
331 error_callback
.Run();
334 dbus::MessageReader
reader(response
);
335 std::vector
<std::string
> device_paths
;
336 if (!reader
.PopArrayOfStrings(&device_paths
)) {
337 LOG(ERROR
) << "Invalid response: " << response
->ToString();
338 error_callback
.Run();
341 callback
.Run(device_paths
);
344 // Handles the result of EnumerateMountEntries and calls |callback| or
346 void OnEnumerateMountEntries(
347 const EnumerateMountEntriesCallback
& callback
,
348 const base::Closure
& error_callback
,
349 dbus::Response
* response
) {
351 error_callback
.Run();
355 dbus::MessageReader
reader(response
);
356 dbus::MessageReader
array_reader(NULL
);
357 if (!reader
.PopArray(&array_reader
)) {
358 LOG(ERROR
) << "Invalid response: " << response
->ToString();
359 error_callback
.Run();
363 std::vector
<MountEntry
> entries
;
364 while (array_reader
.HasMoreData()) {
366 dbus::MessageReader
sub_reader(NULL
);
367 if (!array_reader
.PopStruct(&sub_reader
) ||
368 !ReadMountEntryFromDbus(&sub_reader
, &entry
)) {
369 LOG(ERROR
) << "Invalid response: " << response
->ToString();
370 error_callback
.Run();
373 entries
.push_back(entry
);
375 callback
.Run(entries
);
378 // Handles the result of Format and calls |callback| or |error_callback|.
379 void OnFormat(const base::Closure
& callback
,
380 const base::Closure
& error_callback
,
381 dbus::Response
* response
) {
383 error_callback
.Run();
389 // Handles the result of GetDeviceProperties and calls |callback| or
391 void OnGetDeviceProperties(const std::string
& device_path
,
392 const GetDevicePropertiesCallback
& callback
,
393 const base::Closure
& error_callback
,
394 dbus::Response
* response
) {
396 error_callback
.Run();
399 DiskInfo
disk(device_path
, response
);
403 // Handles mount event signals and calls |handler|.
404 void OnMountEvent(MountEventType event_type
,
405 MountEventHandler handler
,
406 dbus::Signal
* signal
) {
407 dbus::MessageReader
reader(signal
);
409 if (!reader
.PopString(&device
)) {
410 LOG(ERROR
) << "Invalid signal: " << signal
->ToString();
413 handler
.Run(event_type
, device
);
416 // Handles MountCompleted signal and calls |handler|.
417 void OnMountCompleted(MountCompletedHandler handler
, dbus::Signal
* signal
) {
418 dbus::MessageReader
reader(signal
);
420 if (!ReadMountEntryFromDbus(&reader
, &entry
)) {
421 LOG(ERROR
) << "Invalid signal: " << signal
->ToString();
427 // Handles FormatCompleted signal and calls |handler|.
428 void OnFormatCompleted(FormatCompletedHandler handler
, dbus::Signal
* signal
) {
429 dbus::MessageReader
reader(signal
);
430 uint32 error_code
= 0;
431 std::string device_path
;
432 if (!reader
.PopUint32(&error_code
) || !reader
.PopString(&device_path
)) {
433 LOG(ERROR
) << "Invalid signal: " << signal
->ToString();
436 handler
.Run(static_cast<FormatError
>(error_code
), device_path
);
439 // Handles the result of signal connection setup.
440 void OnSignalConnected(const std::string
& interface
,
441 const std::string
& signal
,
443 LOG_IF(ERROR
, !succeeded
) << "Connect to " << interface
<< " " <<
444 signal
<< " failed.";
447 dbus::ObjectProxy
* proxy_
;
449 // Note: This should remain the last member so it'll be destroyed and
450 // invalidate its weak pointers before any other members are destroyed.
451 base::WeakPtrFactory
<CrosDisksClientImpl
> weak_ptr_factory_
;
453 DISALLOW_COPY_AND_ASSIGN(CrosDisksClientImpl
);
458 ////////////////////////////////////////////////////////////////////////////////
461 DiskInfo::DiskInfo(const std::string
& device_path
, dbus::Response
* response
)
462 : device_path_(device_path
),
465 on_boot_device_(false),
466 on_removable_device_(false),
467 device_type_(DEVICE_TYPE_UNKNOWN
),
468 total_size_in_bytes_(0),
469 is_read_only_(false),
471 InitializeFromResponse(response
);
474 DiskInfo::~DiskInfo() {
477 // Initializes |this| from |response| given by the cros-disks service.
478 // Below is an example of |response|'s raw message (long string is ellipsized).
481 // message_type: MESSAGE_METHOD_RETURN
490 // string "DeviceFile"
491 // variant string "/dev/sdb"
494 // string "DeviceIsDrive"
498 // string "DeviceIsMediaAvailable"
502 // string "DeviceIsMounted"
503 // variant bool false
506 // string "DeviceIsOnBootDevice"
507 // variant bool false
510 // string "DeviceIsOnRemovableDevice"
514 // string "DeviceIsReadOnly"
515 // variant bool false
518 // string "DeviceIsVirtual"
519 // variant bool false
522 // string "DeviceMediaType"
526 // string "DeviceMountPaths"
531 // string "DevicePresentationHide"
535 // string "DeviceSize"
536 // variant uint64 7998537728
539 // string "DriveIsRotational"
540 // variant bool false
544 // variant string "18d1"
547 // string "VendorName"
548 // variant string "Google Inc."
551 // string "ProductId"
552 // variant string "4e11"
555 // string "ProductName"
556 // variant string "Nexus One"
559 // string "DriveModel"
560 // variant string "TransMemory"
571 // string "NativePath"
572 // variant string "/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/...
575 void DiskInfo::InitializeFromResponse(dbus::Response
* response
) {
576 dbus::MessageReader
reader(response
);
577 scoped_ptr
<base::Value
> value(dbus::PopDataAsValue(&reader
));
578 base::DictionaryValue
* properties
= NULL
;
579 if (!value
|| !value
->GetAsDictionary(&properties
))
582 properties
->GetBooleanWithoutPathExpansion(
583 cros_disks::kDeviceIsDrive
, &is_drive_
);
584 properties
->GetBooleanWithoutPathExpansion(
585 cros_disks::kDeviceIsReadOnly
, &is_read_only_
);
586 properties
->GetBooleanWithoutPathExpansion(
587 cros_disks::kDevicePresentationHide
, &is_hidden_
);
588 properties
->GetBooleanWithoutPathExpansion(
589 cros_disks::kDeviceIsMediaAvailable
, &has_media_
);
590 properties
->GetBooleanWithoutPathExpansion(
591 cros_disks::kDeviceIsOnBootDevice
, &on_boot_device_
);
592 properties
->GetBooleanWithoutPathExpansion(
593 cros_disks::kDeviceIsOnRemovableDevice
, &on_removable_device_
);
594 properties
->GetStringWithoutPathExpansion(
595 cros_disks::kNativePath
, &system_path_
);
596 properties
->GetStringWithoutPathExpansion(
597 cros_disks::kDeviceFile
, &file_path_
);
598 properties
->GetStringWithoutPathExpansion(cros_disks::kVendorId
, &vendor_id_
);
599 properties
->GetStringWithoutPathExpansion(
600 cros_disks::kVendorName
, &vendor_name_
);
601 properties
->GetStringWithoutPathExpansion(
602 cros_disks::kProductId
, &product_id_
);
603 properties
->GetStringWithoutPathExpansion(
604 cros_disks::kProductName
, &product_name_
);
605 properties
->GetStringWithoutPathExpansion(
606 cros_disks::kDriveModel
, &drive_model_
);
607 properties
->GetStringWithoutPathExpansion(cros_disks::kIdLabel
, &label_
);
608 properties
->GetStringWithoutPathExpansion(cros_disks::kIdUuid
, &uuid_
);
610 // dbus::PopDataAsValue() pops uint64 as double.
611 // The top 11 bits of uint64 are dropped by the use of double. But, this works
612 // unless the size exceeds 8 PB.
613 double device_size_double
= 0;
614 if (properties
->GetDoubleWithoutPathExpansion(cros_disks::kDeviceSize
,
615 &device_size_double
))
616 total_size_in_bytes_
= device_size_double
;
618 // dbus::PopDataAsValue() pops uint32 as double.
619 double media_type_double
= 0;
620 if (properties
->GetDoubleWithoutPathExpansion(cros_disks::kDeviceMediaType
,
622 device_type_
= DeviceMediaTypeToDeviceType(media_type_double
);
624 base::ListValue
* mount_paths
= NULL
;
625 if (properties
->GetListWithoutPathExpansion(cros_disks::kDeviceMountPaths
,
627 mount_paths
->GetString(0, &mount_path_
);
630 ////////////////////////////////////////////////////////////////////////////////
633 CrosDisksClient::CrosDisksClient() {}
635 CrosDisksClient::~CrosDisksClient() {}
638 CrosDisksClient
* CrosDisksClient::Create(DBusClientImplementationType type
) {
639 if (type
== REAL_DBUS_CLIENT_IMPLEMENTATION
)
640 return new CrosDisksClientImpl();
641 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION
, type
);
642 return new FakeCrosDisksClient();
646 base::FilePath
CrosDisksClient::GetArchiveMountPoint() {
647 return base::FilePath(base::SysInfo::IsRunningOnChromeOS() ?
648 FILE_PATH_LITERAL("/media/archive") :
649 FILE_PATH_LITERAL("/tmp/chromeos/media/archive"));
653 base::FilePath
CrosDisksClient::GetRemovableDiskMountPoint() {
654 return base::FilePath(base::SysInfo::IsRunningOnChromeOS() ?
655 FILE_PATH_LITERAL("/media/removable") :
656 FILE_PATH_LITERAL("/tmp/chromeos/media/removable"));
659 } // namespace chromeos