Home | History | Annotate | Download | only in storage_monitor
      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/media_transfer_protocol_device_observer_linux.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/files/file_path.h"
     10 #include "base/stl_util.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_split.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "components/storage_monitor/media_storage_util.h"
     15 #include "components/storage_monitor/removable_device_constants.h"
     16 #include "device/media_transfer_protocol/mtp_storage_info.pb.h"
     17 
     18 namespace storage_monitor {
     19 
     20 namespace {
     21 
     22 // Device root path constant.
     23 const char kRootPath[] = "/";
     24 
     25 // Constructs and returns the location of the device using the |storage_name|.
     26 std::string GetDeviceLocationFromStorageName(const std::string& storage_name) {
     27   // Construct a dummy device path using the storage name. This is only used
     28   // for registering device media file system.
     29   // E.g.: If the |storage_name| is "usb:2,2:12345" then "/usb:2,2:12345" is the
     30   // device location.
     31   DCHECK(!storage_name.empty());
     32   return kRootPath + storage_name;
     33 }
     34 
     35 // Returns the storage identifier of the device from the given |storage_name|.
     36 // E.g. If the |storage_name| is "usb:2,2:65537", the storage identifier is
     37 // "65537".
     38 std::string GetStorageIdFromStorageName(const std::string& storage_name) {
     39   std::vector<std::string> name_parts;
     40   base::SplitString(storage_name, ':', &name_parts);
     41   return name_parts.size() == 3 ? name_parts[2] : std::string();
     42 }
     43 
     44 // Returns a unique device id from the given |storage_info|.
     45 std::string GetDeviceIdFromStorageInfo(const MtpStorageInfo& storage_info) {
     46   const std::string storage_id =
     47       GetStorageIdFromStorageName(storage_info.storage_name());
     48   if (storage_id.empty())
     49     return std::string();
     50 
     51   // Some devices have multiple data stores. Therefore, include storage id as
     52   // part of unique id along with vendor, model and volume information.
     53   const std::string vendor_id = base::UintToString(storage_info.vendor_id());
     54   const std::string model_id = base::UintToString(storage_info.product_id());
     55   return StorageInfo::MakeDeviceId(
     56       StorageInfo::MTP_OR_PTP,
     57       kVendorModelVolumeStoragePrefix + vendor_id + ":" + model_id + ":" +
     58           storage_info.volume_identifier() + ":" + storage_id);
     59 }
     60 
     61 // Returns the |data_store_id| string in the required format.
     62 // If the |data_store_id| is 65537, this function returns " (65537)".
     63 std::string GetFormattedIdString(const std::string& data_store_id) {
     64   return ("(" + data_store_id + ")");
     65 }
     66 
     67 // Helper function to get device label from storage information.
     68 base::string16 GetDeviceLabelFromStorageInfo(
     69     const MtpStorageInfo& storage_info) {
     70   std::string device_label;
     71   const std::string& vendor_name = storage_info.vendor();
     72   device_label = vendor_name;
     73 
     74   const std::string& product_name = storage_info.product();
     75   if (!product_name.empty()) {
     76     if (!device_label.empty())
     77       device_label += " ";
     78     device_label += product_name;
     79   }
     80 
     81   // Add the data store id to the device label.
     82   if (!device_label.empty()) {
     83     const std::string& volume_id = storage_info.volume_identifier();
     84     if (!volume_id.empty()) {
     85       device_label += GetFormattedIdString(volume_id);
     86     } else {
     87       const std::string data_store_id =
     88           GetStorageIdFromStorageName(storage_info.storage_name());
     89       if (!data_store_id.empty())
     90         device_label += GetFormattedIdString(data_store_id);
     91     }
     92   }
     93   return base::UTF8ToUTF16(device_label);
     94 }
     95 
     96 // Helper function to get the device storage details such as device id, label
     97 // and location. On success and fills in |id|, |label|, |location|,
     98 // |vendor_name|, and |product_name|.
     99 void GetStorageInfo(const std::string& storage_name,
    100                     device::MediaTransferProtocolManager* mtp_manager,
    101                     std::string* id,
    102                     base::string16* label,
    103                     std::string* location,
    104                     base::string16* vendor_name,
    105                     base::string16* product_name) {
    106   DCHECK(!storage_name.empty());
    107   const MtpStorageInfo* storage_info =
    108       mtp_manager->GetStorageInfo(storage_name);
    109 
    110   if (!storage_info)
    111     return;
    112 
    113   *id = GetDeviceIdFromStorageInfo(*storage_info);
    114   *label = GetDeviceLabelFromStorageInfo(*storage_info);
    115   *location = GetDeviceLocationFromStorageName(storage_name);
    116   *vendor_name = base::UTF8ToUTF16(storage_info->vendor());
    117   *product_name = base::UTF8ToUTF16(storage_info->product());
    118 }
    119 
    120 }  // namespace
    121 
    122 MediaTransferProtocolDeviceObserverLinux::
    123 MediaTransferProtocolDeviceObserverLinux(
    124     StorageMonitor::Receiver* receiver,
    125     device::MediaTransferProtocolManager* mtp_manager)
    126     : mtp_manager_(mtp_manager),
    127       get_storage_info_func_(&GetStorageInfo),
    128       notifications_(receiver) {
    129   mtp_manager_->AddObserver(this);
    130   EnumerateStorages();
    131 }
    132 
    133 // This constructor is only used by unit tests.
    134 MediaTransferProtocolDeviceObserverLinux::
    135 MediaTransferProtocolDeviceObserverLinux(
    136     StorageMonitor::Receiver* receiver,
    137     device::MediaTransferProtocolManager* mtp_manager,
    138     GetStorageInfoFunc get_storage_info_func)
    139     : mtp_manager_(mtp_manager),
    140       get_storage_info_func_(get_storage_info_func),
    141       notifications_(receiver) {
    142 }
    143 
    144 MediaTransferProtocolDeviceObserverLinux::
    145 ~MediaTransferProtocolDeviceObserverLinux() {
    146   mtp_manager_->RemoveObserver(this);
    147 }
    148 
    149 bool MediaTransferProtocolDeviceObserverLinux::GetStorageInfoForPath(
    150     const base::FilePath& path,
    151     StorageInfo* storage_info) const {
    152   DCHECK(storage_info);
    153 
    154   if (!path.IsAbsolute())
    155     return false;
    156 
    157   std::vector<base::FilePath::StringType> path_components;
    158   path.GetComponents(&path_components);
    159   if (path_components.size() < 2)
    160     return false;
    161 
    162   // First and second component of the path specifies the device location.
    163   // E.g.: If |path| is "/usb:2,2:65537/DCIM/Folder_a", "/usb:2,2:65537" is the
    164   // device location.
    165   StorageLocationToInfoMap::const_iterator info_it =
    166       storage_map_.find(GetDeviceLocationFromStorageName(path_components[1]));
    167   if (info_it == storage_map_.end())
    168     return false;
    169 
    170   *storage_info = info_it->second;
    171   return true;
    172 }
    173 
    174 void MediaTransferProtocolDeviceObserverLinux::EjectDevice(
    175     const std::string& device_id,
    176     base::Callback<void(StorageMonitor::EjectStatus)> callback) {
    177   std::string location;
    178   if (!GetLocationForDeviceId(device_id, &location)) {
    179     callback.Run(StorageMonitor::EJECT_NO_SUCH_DEVICE);
    180     return;
    181   }
    182 
    183   // TODO(thestig): Change this to tell the mtp manager to eject the device.
    184 
    185   StorageChanged(false, location);
    186   callback.Run(StorageMonitor::EJECT_OK);
    187 }
    188 
    189 // device::MediaTransferProtocolManager::Observer override.
    190 void MediaTransferProtocolDeviceObserverLinux::StorageChanged(
    191     bool is_attached,
    192     const std::string& storage_name) {
    193   DCHECK(!storage_name.empty());
    194 
    195   // New storage is attached.
    196   if (is_attached) {
    197     std::string device_id;
    198     base::string16 storage_label;
    199     std::string location;
    200     base::string16 vendor_name;
    201     base::string16 product_name;
    202     get_storage_info_func_(storage_name, mtp_manager_,
    203                            &device_id, &storage_label, &location,
    204                            &vendor_name, &product_name);
    205 
    206     // Keep track of device id and device name to see how often we receive
    207     // empty values.
    208     MediaStorageUtil::RecordDeviceInfoHistogram(false, device_id,
    209                                                 storage_label);
    210     if (device_id.empty() || storage_label.empty())
    211       return;
    212 
    213     DCHECK(!ContainsKey(storage_map_, location));
    214 
    215     StorageInfo storage_info(device_id, location, storage_label,
    216                              vendor_name, product_name, 0);
    217     storage_map_[location] = storage_info;
    218     notifications_->ProcessAttach(storage_info);
    219   } else {
    220     // Existing storage is detached.
    221     StorageLocationToInfoMap::iterator it =
    222         storage_map_.find(GetDeviceLocationFromStorageName(storage_name));
    223     if (it == storage_map_.end())
    224       return;
    225     notifications_->ProcessDetach(it->second.device_id());
    226     storage_map_.erase(it);
    227   }
    228 }
    229 
    230 void MediaTransferProtocolDeviceObserverLinux::EnumerateStorages() {
    231   typedef std::vector<std::string> StorageList;
    232   StorageList storages = mtp_manager_->GetStorages();
    233   for (StorageList::const_iterator storage_iter = storages.begin();
    234        storage_iter != storages.end(); ++storage_iter) {
    235     StorageChanged(true, *storage_iter);
    236   }
    237 }
    238 
    239 bool MediaTransferProtocolDeviceObserverLinux::GetLocationForDeviceId(
    240     const std::string& device_id, std::string* location) const {
    241   for (StorageLocationToInfoMap::const_iterator it = storage_map_.begin();
    242        it != storage_map_.end(); ++it) {
    243     if (it->second.device_id() == device_id) {
    244       *location = it->first;
    245       return true;
    246     }
    247   }
    248 
    249   return false;
    250 }
    251 
    252 }  // namespace storage_monitor
    253