Home | History | Annotate | Download | only in storage_monitor
      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.
      4 
      5 // chromeos::StorageMonitorCros implementation.
      6 
      7 #include "chrome/browser/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 "chrome/browser/storage_monitor/media_storage_util.h"
     17 #include "chrome/browser/storage_monitor/media_transfer_protocol_device_observer_linux.h"
     18 #include "chrome/browser/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 chrome::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 chrome::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                    chrome::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   string16 device_label = UTF8ToUTF16(disk->device_label());
     62   chrome::MediaStorageUtil::RecordDeviceInfoHistogram(true, unique_id,
     63                                                       device_label);
     64   if (unique_id.empty())
     65     return false;
     66 
     67   chrome::StorageInfo::Type type = has_dcim ?
     68       chrome::StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM :
     69       chrome::StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM;
     70 
     71   *info = chrome::StorageInfo(
     72       chrome::StorageInfo::MakeDeviceId(type, unique_id),
     73       string16(),
     74       mount_info.mount_path,
     75       device_label,
     76       UTF8ToUTF16(disk->vendor_name()),
     77       UTF8ToUTF16(disk->product_name()),
     78       disk->total_size_in_bytes());
     79   return true;
     80 }
     81 
     82 // Returns whether the mount point in |mount_info| is a media device or not.
     83 bool CheckMountedPathOnFileThread(
     84     const disks::DiskMountManager::MountPointInfo& mount_info) {
     85   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
     86   return chrome::MediaStorageUtil::HasDcim(
     87       base::FilePath(mount_info.mount_path));
     88 }
     89 
     90 }  // namespace
     91 
     92 using content::BrowserThread;
     93 using chrome::StorageInfo;
     94 
     95 StorageMonitorCros::StorageMonitorCros()
     96     : weak_ptr_factory_(this) {
     97 }
     98 
     99 StorageMonitorCros::~StorageMonitorCros() {
    100   disks::DiskMountManager* manager = disks::DiskMountManager::GetInstance();
    101   if (manager) {
    102     manager->RemoveObserver(this);
    103   }
    104 }
    105 
    106 void StorageMonitorCros::Init() {
    107   DCHECK(disks::DiskMountManager::GetInstance());
    108   disks::DiskMountManager::GetInstance()->AddObserver(this);
    109   CheckExistingMountPoints();
    110 
    111   if (!media_transfer_protocol_manager_) {
    112     scoped_refptr<base::MessageLoopProxy> loop_proxy;
    113     media_transfer_protocol_manager_.reset(
    114         device::MediaTransferProtocolManager::Initialize(loop_proxy));
    115   }
    116 
    117   media_transfer_protocol_device_observer_.reset(
    118       new chrome::MediaTransferProtocolDeviceObserverLinux(
    119           receiver(), media_transfer_protocol_manager_.get()));
    120 }
    121 
    122 void StorageMonitorCros::CheckExistingMountPoints() {
    123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    124   const disks::DiskMountManager::MountPointMap& mount_point_map =
    125       disks::DiskMountManager::GetInstance()->mount_points();
    126   for (disks::DiskMountManager::MountPointMap::const_iterator it =
    127            mount_point_map.begin(); it != mount_point_map.end(); ++it) {
    128     BrowserThread::PostTaskAndReplyWithResult(
    129         BrowserThread::FILE, FROM_HERE,
    130         base::Bind(&CheckMountedPathOnFileThread, it->second),
    131         base::Bind(&StorageMonitorCros::AddMountedPath,
    132                    weak_ptr_factory_.GetWeakPtr(), it->second));
    133   }
    134 
    135   // Note: relies on scheduled tasks on the file thread being sequential. This
    136   // block needs to follow the for loop, so that the DoNothing call on the FILE
    137   // thread happens after the scheduled metadata retrievals, meaning that the
    138   // reply callback will then happen after all the AddNewMount calls.
    139   BrowserThread::PostTaskAndReply(
    140       BrowserThread::FILE, FROM_HERE,
    141       base::Bind(&base::DoNothing),
    142       base::Bind(&StorageMonitorCros::MarkInitialized,
    143                  weak_ptr_factory_.GetWeakPtr()));
    144 }
    145 
    146 void StorageMonitorCros::OnDiskEvent(
    147     disks::DiskMountManager::DiskEvent event,
    148     const disks::DiskMountManager::Disk* disk) {
    149 }
    150 
    151 void StorageMonitorCros::OnDeviceEvent(
    152     disks::DiskMountManager::DeviceEvent event,
    153     const std::string& device_path) {
    154 }
    155 
    156 void StorageMonitorCros::OnMountEvent(
    157     disks::DiskMountManager::MountEvent event,
    158     MountError error_code,
    159     const disks::DiskMountManager::MountPointInfo& mount_info) {
    160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    161 
    162   // Ignore mount points that are not devices.
    163   if (mount_info.mount_type != MOUNT_TYPE_DEVICE)
    164     return;
    165   // Ignore errors.
    166   if (error_code != MOUNT_ERROR_NONE)
    167     return;
    168   if (mount_info.mount_condition != disks::MOUNT_CONDITION_NONE)
    169     return;
    170 
    171   switch (event) {
    172     case disks::DiskMountManager::MOUNTING: {
    173       if (ContainsKey(mount_map_, mount_info.mount_path)) {
    174         NOTREACHED();
    175         return;
    176       }
    177 
    178       BrowserThread::PostTaskAndReplyWithResult(
    179           BrowserThread::FILE, FROM_HERE,
    180           base::Bind(&CheckMountedPathOnFileThread, mount_info),
    181           base::Bind(&StorageMonitorCros::AddMountedPath,
    182                      weak_ptr_factory_.GetWeakPtr(), mount_info));
    183       break;
    184     }
    185     case disks::DiskMountManager::UNMOUNTING: {
    186       MountMap::iterator it = mount_map_.find(mount_info.mount_path);
    187       if (it == mount_map_.end())
    188         return;
    189       receiver()->ProcessDetach(it->second.device_id());
    190       mount_map_.erase(it);
    191       break;
    192     }
    193   }
    194 }
    195 
    196 void StorageMonitorCros::OnFormatEvent(
    197     disks::DiskMountManager::FormatEvent event,
    198     FormatError error_code,
    199     const std::string& device_path) {
    200 }
    201 
    202 void StorageMonitorCros::SetMediaTransferProtocolManagerForTest(
    203     device::MediaTransferProtocolManager* test_manager) {
    204   DCHECK(!media_transfer_protocol_manager_);
    205   media_transfer_protocol_manager_.reset(test_manager);
    206 }
    207 
    208 
    209 bool StorageMonitorCros::GetStorageInfoForPath(
    210     const base::FilePath& path,
    211     StorageInfo* device_info) const {
    212   DCHECK(device_info);
    213 
    214   if (media_transfer_protocol_device_observer_->GetStorageInfoForPath(
    215           path, device_info)) {
    216     return true;
    217   }
    218 
    219   if (!path.IsAbsolute())
    220     return false;
    221 
    222   base::FilePath current = path;
    223   while (!ContainsKey(mount_map_, current.value()) &&
    224          current != current.DirName()) {
    225     current = current.DirName();
    226   }
    227 
    228   MountMap::const_iterator info_it = mount_map_.find(current.value());
    229   if (info_it == mount_map_.end())
    230     return false;
    231 
    232   *device_info = info_it->second;
    233   return true;
    234 }
    235 
    236 // Callback executed when the unmount call is run by DiskMountManager.
    237 // Forwards result to |EjectDevice| caller.
    238 void NotifyUnmountResult(
    239     base::Callback<void(chrome::StorageMonitor::EjectStatus)> callback,
    240     chromeos::MountError error_code) {
    241   if (error_code == MOUNT_ERROR_NONE)
    242     callback.Run(chrome::StorageMonitor::EJECT_OK);
    243   else
    244     callback.Run(chrome::StorageMonitor::EJECT_FAILURE);
    245 }
    246 
    247 void StorageMonitorCros::EjectDevice(
    248     const std::string& device_id,
    249     base::Callback<void(EjectStatus)> callback) {
    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   disks::DiskMountManager* manager = disks::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 disks::DiskMountManager::MountPointInfo& mount_info, bool has_dcim) {
    279   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    280 
    281   if (ContainsKey(mount_map_, mount_info.mount_path)) {
    282     // CheckExistingMountPointsOnUIThread() added the mount point information
    283     // in the map before the device attached handler is called. Therefore, an
    284     // entry for the device already exists in the map.
    285     return;
    286   }
    287 
    288   // Get the media device uuid and label if exists.
    289   chrome::StorageInfo info;
    290   if (!GetDeviceInfo(mount_info, has_dcim, &info))
    291     return;
    292 
    293   if (info.device_id().empty())
    294     return;
    295 
    296   mount_map_.insert(std::make_pair(mount_info.mount_path, info));
    297 
    298   receiver()->ProcessAttach(info);
    299 }
    300 
    301 }  // namespace chromeos
    302 
    303 namespace chrome {
    304 
    305 StorageMonitor* StorageMonitor::Create() {
    306   return new chromeos::StorageMonitorCros();
    307 }
    308 
    309 }  // namespace chrome
    310