[M120 Migration][VD] Enable direct rendering for TVPlus
[platform/framework/web/chromium-efl.git] / components / storage_monitor / storage_monitor_chromeos.cc
1 // Copyright 2014 The Chromium Authors
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 <string>
8 #include <utility>
9
10 #include "base/containers/contains.h"
11 #include "base/files/file_path.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback_helpers.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/task/sequenced_task_runner.h"
16 #include "base/task/task_traits.h"
17 #include "base/task/thread_pool.h"
18 #include "chromeos/ash/components/disks/disk.h"
19 #include "components/storage_monitor/media_storage_util.h"
20 #include "components/storage_monitor/mtp_manager_client_chromeos.h"
21 #include "components/storage_monitor/removable_device_constants.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/device_service.h"
24
25 namespace storage_monitor {
26
27 namespace {
28
29 using ::ash::disks::Disk;
30 using ::ash::disks::DiskMountManager;
31
32 // Constructs a device id using uuid or manufacturer (vendor and product) id
33 // details.
34 std::string MakeDeviceUniqueId(const Disk& disk) {
35   std::string uuid = disk.fs_uuid();
36   if (!uuid.empty())
37     return kFSUniqueIdPrefix + uuid;
38
39   // If one of the vendor or product information is missing, its value in the
40   // string is empty.
41   // Format: VendorModelSerial:VendorInfo:ModelInfo:SerialInfo
42   // TODO(kmadhusu) Extract serial information for the disks and append it to
43   // the device unique id.
44   const std::string& vendor = disk.vendor_id();
45   const std::string& product = disk.product_id();
46   if (vendor.empty() && product.empty())
47     return std::string();
48   return kVendorModelSerialPrefix + vendor + ":" + product + ":";
49 }
50
51 // Returns whether the requested device is valid. On success |info| will contain
52 // device information.
53 bool GetDeviceInfo(const DiskMountManager::MountPoint& mount_info,
54                    bool has_dcim,
55                    StorageInfo* info) {
56   DCHECK(info);
57   std::string source_path = mount_info.source_path;
58
59   const Disk* disk =
60       DiskMountManager::GetInstance()->FindDiskBySourcePath(source_path);
61   if (!disk || disk->device_type() == ash::DeviceType::kUnknown)
62     return false;
63
64   std::string unique_id = MakeDeviceUniqueId(*disk);
65   if (unique_id.empty())
66     return false;
67
68   StorageInfo::Type type = has_dcim ?
69       StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM :
70       StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM;
71
72   *info = StorageInfo(
73       StorageInfo::MakeDeviceId(type, unique_id), mount_info.mount_path,
74       base::UTF8ToUTF16(disk->device_label()),
75       base::UTF8ToUTF16(disk->vendor_name()),
76       base::UTF8ToUTF16(disk->product_name()), disk->total_size_in_bytes());
77   return true;
78 }
79
80 // Returns whether the requested device is valid. On success |info| will contain
81 // fixed storage device information.
82 bool GetFixedStorageInfo(const Disk& disk, StorageInfo* info) {
83   DCHECK(info);
84
85   std::string unique_id = MakeDeviceUniqueId(disk);
86   if (unique_id.empty())
87     return false;
88
89   *info = StorageInfo(
90       StorageInfo::MakeDeviceId(StorageInfo::FIXED_MASS_STORAGE, unique_id),
91       disk.mount_path(), base::UTF8ToUTF16(disk.device_label()),
92       base::UTF8ToUTF16(disk.vendor_name()),
93       base::UTF8ToUTF16(disk.product_name()), disk.total_size_in_bytes());
94   return true;
95 }
96
97 }  // namespace
98
99 StorageMonitorCros::StorageMonitorCros() = default;
100
101 StorageMonitorCros::~StorageMonitorCros() {
102   DiskMountManager* manager = DiskMountManager::GetInstance();
103   if (manager) {
104     manager->RemoveObserver(this);
105   }
106 }
107
108 void StorageMonitorCros::Init() {
109   DCHECK(DiskMountManager::GetInstance());
110   DiskMountManager::GetInstance()->AddObserver(this);
111   CheckExistingMountPoints();
112
113   // Tests may have already set a MTP manager.
114   if (!mtp_device_manager_) {
115     // Set up the connection with mojofied MtpManager.
116     content::GetDeviceService().BindMtpManager(
117         mtp_device_manager_.BindNewPipeAndPassReceiver());
118   }
119   // |mtp_manager_client_| needs to be initialized for both tests and
120   // production code, so keep it out of the if condition.
121   mtp_manager_client_ = std::make_unique<MtpManagerClientChromeOS>(
122       receiver(), mtp_device_manager_.get());
123 }
124
125 void StorageMonitorCros::CheckExistingMountPoints() {
126   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
127
128   for (const auto& disk : DiskMountManager::GetInstance()->disks()) {
129     if (disk->IsStatefulPartition()) {
130       AddFixedStorageDisk(*disk);
131       break;
132     }
133   }
134
135   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
136       base::ThreadPool::CreateSequencedTaskRunner(
137           {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
138
139   for (const auto& mount_point :
140        DiskMountManager::GetInstance()->mount_points()) {
141     blocking_task_runner->PostTaskAndReplyWithResult(
142         FROM_HERE,
143         base::BindOnce(&MediaStorageUtil::HasDcim,
144                        base::FilePath(mount_point.mount_path)),
145         base::BindOnce(&StorageMonitorCros::AddMountedPath,
146                        weak_ptr_factory_.GetWeakPtr(), mount_point));
147   }
148
149   // Note: Relies on scheduled tasks on the |blocking_task_runner| being
150   // sequential. This block needs to follow the for loop, so that the DoNothing
151   // call on the |blocking_task_runner| happens after the scheduled metadata
152   // retrievals, meaning that the reply callback will then happen after all the
153   // AddMountedPath calls.
154
155   blocking_task_runner->PostTaskAndReply(
156       FROM_HERE, base::DoNothing(),
157       base::BindOnce(&StorageMonitorCros::MarkInitialized,
158                      weak_ptr_factory_.GetWeakPtr()));
159 }
160
161 void StorageMonitorCros::OnBootDeviceDiskEvent(
162     DiskMountManager::DiskEvent event,
163     const Disk& disk) {
164   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
165
166   if (!disk.IsStatefulPartition())
167     return;
168
169   switch (event) {
170     case DiskMountManager::DiskEvent::DISK_ADDED: {
171       AddFixedStorageDisk(disk);
172       break;
173     }
174     case DiskMountManager::DiskEvent::DISK_REMOVED: {
175       RemoveFixedStorageDisk(disk);
176       break;
177     }
178     case DiskMountManager::DiskEvent::DISK_CHANGED: {
179       // Although boot disks never change, this event is fired when the device
180       // is woken from suspend and re-enumerates the set of disks. The event
181       // could be changed to only fire when an actual change occurs, but that's
182       // not currently possible because the "re-enumerate on wake" behaviour is
183       // relied on to re-mount external media that was unmounted when the system
184       // was suspended.
185       break;
186     }
187   }
188 }
189
190 void StorageMonitorCros::OnMountEvent(
191     DiskMountManager::MountEvent event,
192     ash::MountError error_code,
193     const DiskMountManager::MountPoint& mount_info) {
194   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
195
196   // Ignore mount points that are not devices.
197   if (mount_info.mount_type != ash::MountType::kDevice)
198     return;
199   // Ignore errors.
200   if (error_code != ash::MountError::kSuccess)
201     return;
202   if (mount_info.mount_error != ash::MountError::kSuccess)
203     return;
204
205   switch (event) {
206     case DiskMountManager::MOUNTING: {
207       if (base::Contains(mount_map_, mount_info.mount_path)) {
208         return;
209       }
210
211       base::ThreadPool::PostTaskAndReplyWithResult(
212           FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
213           base::BindOnce(&MediaStorageUtil::HasDcim,
214                          base::FilePath(mount_info.mount_path)),
215           base::BindOnce(&StorageMonitorCros::AddMountedPath,
216                          weak_ptr_factory_.GetWeakPtr(), mount_info));
217       break;
218     }
219     case DiskMountManager::UNMOUNTING: {
220       MountMap::iterator it = mount_map_.find(mount_info.mount_path);
221       if (it == mount_map_.end())
222         return;
223       receiver()->ProcessDetach(it->second.device_id());
224       mount_map_.erase(it);
225       break;
226     }
227   }
228 }
229
230 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
231     mojo::PendingRemote<device::mojom::MtpManager> test_manager) {
232   DCHECK(!mtp_device_manager_);
233   mtp_device_manager_.Bind(std::move(test_manager));
234 }
235
236 bool StorageMonitorCros::GetStorageInfoForPath(
237     const base::FilePath& path,
238     StorageInfo* device_info) const {
239   DCHECK(device_info);
240
241   if (mtp_manager_client_->GetStorageInfoForPath(path, device_info)) {
242     return true;
243   }
244
245   if (!path.IsAbsolute())
246     return false;
247
248   base::FilePath current = path;
249   while (!base::Contains(mount_map_, current.value()) &&
250          current != current.DirName()) {
251     current = current.DirName();
252   }
253
254   MountMap::const_iterator info_it = mount_map_.find(current.value());
255   if (info_it == mount_map_.end())
256     return false;
257
258   *device_info = info_it->second;
259   return true;
260 }
261
262 // Callback executed when the unmount call is run by DiskMountManager.
263 // Forwards result to |EjectDevice| caller.
264 void NotifyUnmountResult(
265     base::OnceCallback<void(StorageMonitor::EjectStatus)> callback,
266     ash::MountError error_code) {
267   if (error_code == ash::MountError::kSuccess)
268     std::move(callback).Run(StorageMonitor::EJECT_OK);
269   else
270     std::move(callback).Run(StorageMonitor::EJECT_FAILURE);
271 }
272
273 void StorageMonitorCros::EjectDevice(
274     const std::string& device_id,
275     base::OnceCallback<void(EjectStatus)> callback) {
276   StorageInfo::Type type;
277   if (!StorageInfo::CrackDeviceId(device_id, &type, nullptr)) {
278     std::move(callback).Run(EJECT_FAILURE);
279     return;
280   }
281
282   if (type == StorageInfo::MTP_OR_PTP) {
283     mtp_manager_client_->EjectDevice(device_id, std::move(callback));
284     return;
285   }
286
287   std::string mount_path;
288   for (MountMap::const_iterator info_it = mount_map_.begin();
289        info_it != mount_map_.end(); ++info_it) {
290     if (info_it->second.device_id() == device_id)
291       mount_path = info_it->first;
292   }
293
294   if (mount_path.empty()) {
295     std::move(callback).Run(EJECT_NO_SUCH_DEVICE);
296     return;
297   }
298
299   DiskMountManager* manager = DiskMountManager::GetInstance();
300   if (!manager) {
301     std::move(callback).Run(EJECT_FAILURE);
302     return;
303   }
304
305   manager->UnmountPath(
306       mount_path, base::BindOnce(NotifyUnmountResult, std::move(callback)));
307 }
308
309 device::mojom::MtpManager*
310 StorageMonitorCros::media_transfer_protocol_manager() {
311   return mtp_device_manager_.get();
312 }
313
314 void StorageMonitorCros::AddMountedPath(
315     const DiskMountManager::MountPoint& mount_info,
316     bool has_dcim) {
317   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
318
319   if (base::Contains(mount_map_, mount_info.mount_path)) {
320     // CheckExistingMountPoints() added the mount point information in the map
321     // before the device attached handler is called. Therefore, an entry for
322     // the device already exists in the map.
323     return;
324   }
325
326   // Get the media device uuid and label if exists.
327   StorageInfo info;
328   if (!GetDeviceInfo(mount_info, has_dcim, &info))
329     return;
330
331   if (info.device_id().empty())
332     return;
333
334   mount_map_.insert(std::make_pair(mount_info.mount_path, info));
335
336   receiver()->ProcessAttach(info);
337 }
338
339 void StorageMonitorCros::AddFixedStorageDisk(const Disk& disk) {
340   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
341   DCHECK(disk.IsStatefulPartition());
342
343   StorageInfo info;
344   if (!GetFixedStorageInfo(disk, &info))
345     return;
346
347   if (base::Contains(mount_map_, disk.mount_path()))
348     return;
349
350   mount_map_.insert(std::make_pair(disk.mount_path(), info));
351   receiver()->ProcessAttach(info);
352 }
353
354 void StorageMonitorCros::RemoveFixedStorageDisk(const Disk& disk) {
355   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
356   DCHECK(disk.IsStatefulPartition());
357
358   StorageInfo info;
359   if (!GetFixedStorageInfo(disk, &info))
360     return;
361
362   size_t erased_count = mount_map_.erase(disk.mount_path());
363   if (!erased_count)
364     return;
365
366   receiver()->ProcessDetach((info.device_id()));
367 }
368
369 StorageMonitor* StorageMonitor::CreateInternal() {
370   return new StorageMonitorCros();
371 }
372
373 }  // namespace storage_monitor