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