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 22namespace storage_monitor { 23 24StorageMonitorWin::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 39StorageMonitorWin::~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 52void 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 71bool 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 104void 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 121bool 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 133LRESULT 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 142LRESULT 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 158void 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, ¬ify_entry); 167 if (!shell_change_notify_id_) 168 DVLOG(1) << "SHChangeNotifyRegister FAILED"; 169 } else { 170 DVLOG(1) << "SHGetSpecialFolderLocation FAILED"; 171 } 172} 173 174bool 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 184void 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 190void StorageMonitorWin::OnMediaChange(WPARAM wparam, LPARAM lparam) { 191 volume_mount_watcher_->OnMediaChange(wparam, lparam); 192} 193 194StorageMonitor* StorageMonitor::CreateInternal() { 195 return new StorageMonitorWin(new VolumeMountWatcherWin(), 196 new PortableDeviceWatcherWin()); 197} 198 199} // namespace storage_monitor 200