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/storage_monitor_win.h"
      6 
      7 #include <windows.h>
      8 #include <dbt.h>
      9 #include <fileapi.h>
     10 #include <shlobj.h>
     11 
     12 #include "base/win/wrapped_window_proc.h"
     13 #include "components/storage_monitor/portable_device_watcher_win.h"
     14 #include "components/storage_monitor/removable_device_constants.h"
     15 #include "components/storage_monitor/storage_info.h"
     16 #include "components/storage_monitor/volume_mount_watcher_win.h"
     17 
     18 #define WM_USER_MEDIACHANGED (WM_USER + 5)
     19 
     20 // StorageMonitorWin -------------------------------------------------------
     21 
     22 namespace storage_monitor {
     23 
     24 StorageMonitorWin::StorageMonitorWin(
     25     VolumeMountWatcherWin* volume_mount_watcher,
     26     PortableDeviceWatcherWin* portable_device_watcher)
     27     : window_class_(0),
     28       instance_(NULL),
     29       window_(NULL),
     30       shell_change_notify_id_(0),
     31       volume_mount_watcher_(volume_mount_watcher),
     32       portable_device_watcher_(portable_device_watcher) {
     33   DCHECK(volume_mount_watcher_);
     34   DCHECK(portable_device_watcher_);
     35   volume_mount_watcher_->SetNotifications(receiver());
     36   portable_device_watcher_->SetNotifications(receiver());
     37 }
     38 
     39 StorageMonitorWin::~StorageMonitorWin() {
     40   if (shell_change_notify_id_)
     41     SHChangeNotifyDeregister(shell_change_notify_id_);
     42   volume_mount_watcher_->SetNotifications(NULL);
     43   portable_device_watcher_->SetNotifications(NULL);
     44 
     45   if (window_)
     46     DestroyWindow(window_);
     47 
     48   if (window_class_)
     49     UnregisterClass(MAKEINTATOM(window_class_), instance_);
     50 }
     51 
     52 void StorageMonitorWin::Init() {
     53   WNDCLASSEX window_class;
     54   base::win::InitializeWindowClass(
     55       L"Chrome_StorageMonitorWindow",
     56       &base::win::WrappedWindowProc<StorageMonitorWin::WndProcThunk>,
     57       0, 0, 0, NULL, NULL, NULL, NULL, NULL,
     58       &window_class);
     59   instance_ = window_class.hInstance;
     60   window_class_ = RegisterClassEx(&window_class);
     61   DCHECK(window_class_);
     62 
     63   window_ = CreateWindow(MAKEINTATOM(window_class_), 0, 0, 0, 0, 0, 0, 0, 0,
     64                          instance_, 0);
     65   SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
     66   volume_mount_watcher_->Init();
     67   portable_device_watcher_->Init(window_);
     68   MediaChangeNotificationRegister();
     69 }
     70 
     71 bool StorageMonitorWin::GetStorageInfoForPath(const base::FilePath& path,
     72                                               StorageInfo* device_info) const {
     73   DCHECK(device_info);
     74 
     75   // TODO(gbillock): Move this logic up to StorageMonitor.
     76   // If we already know the StorageInfo for the path, just return it.
     77   // This will account for portable devices as well.
     78   std::vector<StorageInfo> attached_devices = GetAllAvailableStorages();
     79   size_t best_parent = attached_devices.size();
     80   size_t best_length = 0;
     81   for (size_t i = 0; i < attached_devices.size(); i++) {
     82     if (!StorageInfo::IsRemovableDevice(attached_devices[i].device_id()))
     83       continue;
     84     base::FilePath relative;
     85     if (base::FilePath(attached_devices[i].location()).AppendRelativePath(
     86             path, &relative)) {
     87       // Note: the relative path is longer for shorter shared path between
     88       // the path and the device mount point, so we want the shortest
     89       // relative path.
     90       if (relative.value().size() < best_length) {
     91         best_parent = i;
     92         best_length = relative.value().size();
     93       }
     94     }
     95   }
     96   if (best_parent != attached_devices.size()) {
     97     *device_info = attached_devices[best_parent];
     98     return true;
     99   }
    100 
    101   return GetDeviceInfo(path, device_info);
    102 }
    103 
    104 void StorageMonitorWin::EjectDevice(
    105     const std::string& device_id,
    106     base::Callback<void(EjectStatus)> callback) {
    107   StorageInfo::Type type;
    108   if (!StorageInfo::CrackDeviceId(device_id, &type, NULL)) {
    109     callback.Run(EJECT_FAILURE);
    110     return;
    111   }
    112 
    113   if (type == StorageInfo::MTP_OR_PTP)
    114     portable_device_watcher_->EjectDevice(device_id, callback);
    115   else if (StorageInfo::IsRemovableDevice(device_id))
    116     volume_mount_watcher_->EjectDevice(device_id, callback);
    117   else
    118     callback.Run(EJECT_FAILURE);
    119 }
    120 
    121 bool StorageMonitorWin::GetMTPStorageInfoFromDeviceId(
    122     const std::string& storage_device_id,
    123     base::string16* device_location,
    124     base::string16* storage_object_id) const {
    125   StorageInfo::Type type;
    126   StorageInfo::CrackDeviceId(storage_device_id, &type, NULL);
    127   return ((type == StorageInfo::MTP_OR_PTP) &&
    128       portable_device_watcher_->GetMTPStorageInfoFromDeviceId(
    129           storage_device_id, device_location, storage_object_id));
    130 }
    131 
    132 // static
    133 LRESULT CALLBACK StorageMonitorWin::WndProcThunk(HWND hwnd, UINT message,
    134                                                  WPARAM wparam, LPARAM lparam) {
    135   StorageMonitorWin* msg_wnd = reinterpret_cast<StorageMonitorWin*>(
    136       GetWindowLongPtr(hwnd, GWLP_USERDATA));
    137   if (msg_wnd)
    138     return msg_wnd->WndProc(hwnd, message, wparam, lparam);
    139   return ::DefWindowProc(hwnd, message, wparam, lparam);
    140 }
    141 
    142 LRESULT CALLBACK StorageMonitorWin::WndProc(HWND hwnd, UINT message,
    143                                             WPARAM wparam, LPARAM lparam) {
    144   switch (message) {
    145     case WM_DEVICECHANGE:
    146       OnDeviceChange(static_cast<UINT>(wparam), lparam);
    147       return TRUE;
    148     case WM_USER_MEDIACHANGED:
    149       OnMediaChange(wparam, lparam);
    150       return TRUE;
    151     default:
    152       break;
    153   }
    154 
    155   return ::DefWindowProc(hwnd, message, wparam, lparam);
    156 }
    157 
    158 void StorageMonitorWin::MediaChangeNotificationRegister() {
    159   LPITEMIDLIST id_list;
    160   if (SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &id_list) == NOERROR) {
    161     SHChangeNotifyEntry notify_entry;
    162     notify_entry.pidl = id_list;
    163     notify_entry.fRecursive = TRUE;
    164     shell_change_notify_id_ = SHChangeNotifyRegister(
    165         window_, SHCNRF_ShellLevel, SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED,
    166         WM_USER_MEDIACHANGED, 1, &notify_entry);
    167     if (!shell_change_notify_id_)
    168       DVLOG(1) << "SHChangeNotifyRegister FAILED";
    169   } else {
    170     DVLOG(1) << "SHGetSpecialFolderLocation FAILED";
    171   }
    172 }
    173 
    174 bool StorageMonitorWin::GetDeviceInfo(const base::FilePath& device_path,
    175                                       StorageInfo* info) const {
    176   DCHECK(info);
    177 
    178   // TODO(kmadhusu) Implement PortableDeviceWatcherWin::GetDeviceInfo()
    179   // function when we have the functionality to add a sub directory of
    180   // portable device as a media gallery.
    181   return volume_mount_watcher_->GetDeviceInfo(device_path, info);
    182 }
    183 
    184 void StorageMonitorWin::OnDeviceChange(UINT event_type, LPARAM data) {
    185   DVLOG(1) << "OnDeviceChange " << event_type << " " << data;
    186   volume_mount_watcher_->OnWindowMessage(event_type, data);
    187   portable_device_watcher_->OnWindowMessage(event_type, data);
    188 }
    189 
    190 void StorageMonitorWin::OnMediaChange(WPARAM wparam, LPARAM lparam) {
    191   volume_mount_watcher_->OnMediaChange(wparam, lparam);
    192 }
    193 
    194 StorageMonitor* StorageMonitor::CreateInternal() {
    195   return new StorageMonitorWin(new VolumeMountWatcherWin(),
    196                                new PortableDeviceWatcherWin());
    197 }
    198 
    199 }  // namespace storage_monitor
    200