Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / storage_monitor / storage_monitor_chromeos.cc
1 // Copyright 2014 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 // chromeos::StorageMonitorCros implementation.
6
7 #include "components/storage_monitor/storage_monitor_chromeos.h"
8
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/storage_monitor/media_storage_util.h"
17 #include "components/storage_monitor/media_transfer_protocol_device_observer_linux.h"
18 #include "components/storage_monitor/removable_device_constants.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
21
22 namespace chromeos {
23
24 namespace {
25
26 // Constructs a device id using uuid or manufacturer (vendor and product) id
27 // details.
28 std::string MakeDeviceUniqueId(const disks::DiskMountManager::Disk& disk) {
29   std::string uuid = disk.fs_uuid();
30   if (!uuid.empty())
31     return kFSUniqueIdPrefix + uuid;
32
33   // If one of the vendor or product information is missing, its value in the
34   // string is empty.
35   // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo
36   // TODO(kmadhusu) Extract serial information for the disks and append it to
37   // the device unique id.
38   const std::string& vendor = disk.vendor_id();
39   const std::string& product = disk.product_id();
40   if (vendor.empty() && product.empty())
41     return std::string();
42   return kVendorModelSerialPrefix + vendor + ":" + product + ":";
43 }
44
45 // Returns true if the requested device is valid, else false. On success, fills
46 // in |info|.
47 bool GetDeviceInfo(const disks::DiskMountManager::MountPointInfo& mount_info,
48                    bool has_dcim,
49                    StorageInfo* info) {
50   DCHECK(info);
51   std::string source_path = mount_info.source_path;
52
53   const disks::DiskMountManager::Disk* disk =
54       disks::DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path);
55   if (!disk || disk->device_type() == DEVICE_TYPE_UNKNOWN)
56     return false;
57
58   std::string unique_id = MakeDeviceUniqueId(*disk);
59   // Keep track of device uuid and label, to see how often we receive empty
60   // values.
61   base::string16 device_label = base::UTF8ToUTF16(disk->device_label());
62   MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id, device_label);
63   if (unique_id.empty())
64     return false;
65
66   StorageInfo::Type type = has_dcim ?
67       StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM :
68       StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM;
69
70   *info = StorageInfo(StorageInfo::MakeDeviceId(type, unique_id),
71                       base::string16(),
72                       mount_info.mount_path,
73                       device_label,
74                       base::UTF8ToUTF16(disk->vendor_name()),
75                       base::UTF8ToUTF16(disk->product_name()),
76                       disk->total_size_in_bytes());
77   return true;
78 }
79
80 // Returns whether the mount point in |mount_info| is a media device or not.
81 bool CheckMountedPathOnFileThread(
82     const disks::DiskMountManager::MountPointInfo& mount_info) {
83   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
84   return MediaStorageUtil::HasDcim(base::FilePath(mount_info.mount_path));
85 }
86
87 }  // namespace
88
89 using content::BrowserThread;
90
91 StorageMonitorCros::StorageMonitorCros()
92     : weak_ptr_factory_(this) {
93 }
94
95 StorageMonitorCros::~StorageMonitorCros() {
96   disks::DiskMountManager* manager = disks::DiskMountManager::GetInstance();
97   if (manager) {
98     manager->RemoveObserver(this);
99   }
100 }
101
102 void StorageMonitorCros::Init() {
103   DCHECK(disks::DiskMountManager::GetInstance());
104   disks::DiskMountManager::GetInstance()->AddObserver(this);
105   CheckExistingMountPoints();
106
107   if (!media_transfer_protocol_manager_) {
108     scoped_refptr<base::MessageLoopProxy> loop_proxy;
109     media_transfer_protocol_manager_.reset(
110         device::MediaTransferProtocolManager::Initialize(loop_proxy));
111   }
112
113   media_transfer_protocol_device_observer_.reset(
114       new MediaTransferProtocolDeviceObserverLinux(
115           receiver(), media_transfer_protocol_manager_.get()));
116 }
117
118 void StorageMonitorCros::CheckExistingMountPoints() {
119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120   const disks::DiskMountManager::MountPointMap& mount_point_map =
121       disks::DiskMountManager::GetInstance()->mount_points();
122   for (disks::DiskMountManager::MountPointMap::const_iterator it =
123            mount_point_map.begin(); it != mount_point_map.end(); ++it) {
124     BrowserThread::PostTaskAndReplyWithResult(
125         BrowserThread::FILE, FROM_HERE,
126         base::Bind(&CheckMountedPathOnFileThread, it->second),
127         base::Bind(&StorageMonitorCros::AddMountedPath,
128                    weak_ptr_factory_.GetWeakPtr(), it->second));
129   }
130
131   // Note: relies on scheduled tasks on the file thread being sequential. This
132   // block needs to follow the for loop, so that the DoNothing call on the FILE
133   // thread happens after the scheduled metadata retrievals, meaning that the
134   // reply callback will then happen after all the AddNewMount calls.
135   BrowserThread::PostTaskAndReply(
136       BrowserThread::FILE, FROM_HERE,
137       base::Bind(&base::DoNothing),
138       base::Bind(&StorageMonitorCros::MarkInitialized,
139                  weak_ptr_factory_.GetWeakPtr()));
140 }
141
142 void StorageMonitorCros::OnDiskEvent(
143     disks::DiskMountManager::DiskEvent event,
144     const disks::DiskMountManager::Disk* disk) {
145 }
146
147 void StorageMonitorCros::OnDeviceEvent(
148     disks::DiskMountManager::DeviceEvent event,
149     const std::string& device_path) {
150 }
151
152 void StorageMonitorCros::OnMountEvent(
153     disks::DiskMountManager::MountEvent event,
154     MountError error_code,
155     const disks::DiskMountManager::MountPointInfo& mount_info) {
156   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157
158   // Ignore mount points that are not devices.
159   if (mount_info.mount_type != MOUNT_TYPE_DEVICE)
160     return;
161   // Ignore errors.
162   if (error_code != MOUNT_ERROR_NONE)
163     return;
164   if (mount_info.mount_condition != disks::MOUNT_CONDITION_NONE)
165     return;
166
167   switch (event) {
168     case disks::DiskMountManager::MOUNTING: {
169       if (ContainsKey(mount_map_, mount_info.mount_path)) {
170         NOTREACHED();
171         return;
172       }
173
174       BrowserThread::PostTaskAndReplyWithResult(
175           BrowserThread::FILE, FROM_HERE,
176           base::Bind(&CheckMountedPathOnFileThread, mount_info),
177           base::Bind(&StorageMonitorCros::AddMountedPath,
178                      weak_ptr_factory_.GetWeakPtr(), mount_info));
179       break;
180     }
181     case disks::DiskMountManager::UNMOUNTING: {
182       MountMap::iterator it = mount_map_.find(mount_info.mount_path);
183       if (it == mount_map_.end())
184         return;
185       receiver()->ProcessDetach(it->second.device_id());
186       mount_map_.erase(it);
187       break;
188     }
189   }
190 }
191
192 void StorageMonitorCros::OnFormatEvent(
193     disks::DiskMountManager::FormatEvent event,
194     FormatError error_code,
195     const std::string& device_path) {
196 }
197
198 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
199     device::MediaTransferProtocolManager* test_manager) {
200   DCHECK(!media_transfer_protocol_manager_);
201   media_transfer_protocol_manager_.reset(test_manager);
202 }
203
204
205 bool StorageMonitorCros::GetStorageInfoForPath(
206     const base::FilePath& path,
207     StorageInfo* device_info) const {
208   DCHECK(device_info);
209
210   if (media_transfer_protocol_device_observer_->GetStorageInfoForPath(
211           path, device_info)) {
212     return true;
213   }
214
215   if (!path.IsAbsolute())
216     return false;
217
218   base::FilePath current = path;
219   while (!ContainsKey(mount_map_, current.value()) &&
220          current != current.DirName()) {
221     current = current.DirName();
222   }
223
224   MountMap::const_iterator info_it = mount_map_.find(current.value());
225   if (info_it == mount_map_.end())
226     return false;
227
228   *device_info = info_it->second;
229   return true;
230 }
231
232 // Callback executed when the unmount call is run by DiskMountManager.
233 // Forwards result to |EjectDevice| caller.
234 void NotifyUnmountResult(
235     base::Callback<void(StorageMonitor::EjectStatus)> callback,
236     chromeos::MountError error_code) {
237   if (error_code == MOUNT_ERROR_NONE)
238     callback.Run(StorageMonitor::EJECT_OK);
239   else
240     callback.Run(StorageMonitor::EJECT_FAILURE);
241 }
242
243 void StorageMonitorCros::EjectDevice(
244     const std::string& device_id,
245     base::Callback<void(EjectStatus)> callback) {
246   StorageInfo::Type type;
247   if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) {
248     callback.Run(EJECT_FAILURE);
249     return;
250   }
251
252   if (type == StorageInfo::MTP_OR_PTP) {
253     media_transfer_protocol_device_observer_->EjectDevice(device_id, callback);
254     return;
255   }
256
257   std::string mount_path;
258   for (MountMap::const_iterator info_it = mount_map_.begin();
259        info_it != mount_map_.end(); ++info_it) {
260     if (info_it->second.device_id() == device_id)
261       mount_path = info_it->first;
262   }
263
264   if (mount_path.empty()) {
265     callback.Run(EJECT_NO_SUCH_DEVICE);
266     return;
267   }
268
269   disks::DiskMountManager* manager = disks::DiskMountManager::GetInstance();
270   if (!manager) {
271     callback.Run(EJECT_FAILURE);
272     return;
273   }
274
275   manager->UnmountPath(mount_path, chromeos::UNMOUNT_OPTIONS_NONE,
276                        base::Bind(NotifyUnmountResult, callback));
277 }
278
279 device::MediaTransferProtocolManager*
280 StorageMonitorCros::media_transfer_protocol_manager() {
281   return media_transfer_protocol_manager_.get();
282 }
283
284 void StorageMonitorCros::AddMountedPath(
285     const disks::DiskMountManager::MountPointInfo& mount_info, bool has_dcim) {
286   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287
288   if (ContainsKey(mount_map_, mount_info.mount_path)) {
289     // CheckExistingMountPointsOnUIThread() added the mount point information
290     // in the map before the device attached handler is called. Therefore, an
291     // entry for the device already exists in the map.
292     return;
293   }
294
295   // Get the media device uuid and label if exists.
296   StorageInfo info;
297   if (!GetDeviceInfo(mount_info, has_dcim, &info))
298     return;
299
300   if (info.device_id().empty())
301     return;
302
303   mount_map_.insert(std::make_pair(mount_info.mount_path, info));
304
305   receiver()->ProcessAttach(info);
306 }
307
308 }  // namespace chromeos
309
310 StorageMonitor* StorageMonitor::CreateInternal() {
311   return new chromeos::StorageMonitorCros();
312 }