OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/system_monitor/removable_device_notifications_window_wi
n.h" | 5 #include "chrome/browser/system_monitor/removable_device_notifications_window_wi
n.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <dbt.h> | 8 #include <dbt.h> |
9 | 9 #include <fileapi.h> |
10 #include <string> | |
11 | 10 |
12 #include "base/file_path.h" | 11 #include "base/file_path.h" |
| 12 #include "base/metrics/histogram.h" |
13 #include "base/string_number_conversions.h" | 13 #include "base/string_number_conversions.h" |
14 #include "base/system_monitor/system_monitor.h" | 14 #include "base/system_monitor/system_monitor.h" |
| 15 #include "base/utf_string_conversions.h" |
15 #include "base/win/wrapped_window_proc.h" | 16 #include "base/win/wrapped_window_proc.h" |
16 #include "chrome/browser/system_monitor/media_device_notifications_utils.h" | 17 #include "chrome/browser/system_monitor/media_device_notifications_utils.h" |
17 #include "chrome/browser/system_monitor/media_storage_util.h" | 18 #include "chrome/browser/system_monitor/media_storage_util.h" |
18 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
19 | 20 |
20 using base::SystemMonitor; | 21 using base::SystemMonitor; |
| 22 using base::win::WrappedWindowProc; |
21 using content::BrowserThread; | 23 using content::BrowserThread; |
22 | 24 |
23 namespace { | 25 namespace { |
24 | 26 |
25 const wchar_t WindowClassName[] = L"Chrome_RemovableDeviceNotificationWindow"; | 27 const DWORD kMaxPathBufLen = MAX_PATH + 1; |
26 | 28 |
27 LRESULT GetVolumeName(LPCWSTR drive, | 29 const char16 kWindowClassName[] = L"Chrome_RemovableDeviceNotificationWindow"; |
28 LPWSTR volume_name, | 30 |
29 unsigned int volume_name_len) { | 31 static chrome::RemovableDeviceNotificationsWindowWin* |
30 return GetVolumeInformation(drive, volume_name, volume_name_len, NULL, NULL, | 32 g_removable_device_notifications_window_win = NULL; |
31 NULL, NULL, 0); | 33 |
| 34 // The following msdn blog entry is helpful for understanding disk volumes |
| 35 // and how they are treated in Windows: |
| 36 // http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx |
| 37 bool GetDeviceInfo(const FilePath& device_path, string16* device_location, |
| 38 std::string* unique_id, string16* name, bool* removable) { |
| 39 char16 mount_point[kMaxPathBufLen]; |
| 40 if (!GetVolumePathName(device_path.value().c_str(), mount_point, |
| 41 kMaxPathBufLen)) { |
| 42 return false; |
| 43 } |
| 44 if (device_location) |
| 45 *device_location = string16(mount_point); |
| 46 |
| 47 if (unique_id) { |
| 48 char16 guid[kMaxPathBufLen]; |
| 49 if (!GetVolumeNameForVolumeMountPoint(mount_point, guid, kMaxPathBufLen)) |
| 50 return false; |
| 51 // In case it has two GUID's (see above mentioned blog), do it again. |
| 52 if (!GetVolumeNameForVolumeMountPoint(guid, guid, kMaxPathBufLen)) |
| 53 return false; |
| 54 WideToUTF8(guid, wcslen(guid), unique_id); |
| 55 } |
| 56 |
| 57 if (name) { |
| 58 char16 volume_name[kMaxPathBufLen]; |
| 59 if (!GetVolumeInformation(mount_point, volume_name, kMaxPathBufLen, NULL, |
| 60 NULL, NULL, NULL, 0)) { |
| 61 return false; |
| 62 } |
| 63 if (wcslen(volume_name) > 0) { |
| 64 *name = string16(volume_name); |
| 65 } else { |
| 66 *name = device_path.LossyDisplayName(); |
| 67 } |
| 68 } |
| 69 |
| 70 if (removable) |
| 71 *removable = (GetDriveType(mount_point) == DRIVE_REMOVABLE); |
| 72 |
| 73 return true; |
| 74 } |
| 75 |
| 76 std::vector<FilePath> GetAttachedDevices() { |
| 77 std::vector<FilePath> result; |
| 78 char16 volume_name[kMaxPathBufLen]; |
| 79 HANDLE find_handle = FindFirstVolume(volume_name, kMaxPathBufLen); |
| 80 if (find_handle == INVALID_HANDLE_VALUE) |
| 81 return result; |
| 82 |
| 83 while (true) { |
| 84 char16 volume_path[kMaxPathBufLen]; |
| 85 DWORD return_count; |
| 86 if (GetVolumePathNamesForVolumeName(volume_name, volume_path, |
| 87 kMaxPathBufLen, &return_count)) { |
| 88 if (GetDriveType(volume_path) == DRIVE_REMOVABLE) |
| 89 result.push_back(FilePath(volume_path)); |
| 90 } else { |
| 91 DPLOG(ERROR); |
| 92 } |
| 93 if (!FindNextVolume(find_handle, volume_name, kMaxPathBufLen)) { |
| 94 if (GetLastError() != ERROR_NO_MORE_FILES) |
| 95 DPLOG(ERROR); |
| 96 break; |
| 97 } |
| 98 } |
| 99 |
| 100 FindVolumeClose(find_handle); |
| 101 return result; |
32 } | 102 } |
33 | 103 |
34 // Returns 0 if the devicetype is not volume. | 104 // Returns 0 if the devicetype is not volume. |
35 DWORD GetVolumeBitMaskFromBroadcastHeader(DWORD data) { | 105 uint32 GetVolumeBitMaskFromBroadcastHeader(LPARAM data) { |
36 PDEV_BROADCAST_HDR dev_broadcast_hdr = | 106 DEV_BROADCAST_VOLUME* dev_broadcast_volume = |
37 reinterpret_cast<PDEV_BROADCAST_HDR>(data); | 107 reinterpret_cast<DEV_BROADCAST_VOLUME*>(data); |
38 if (dev_broadcast_hdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { | 108 if (dev_broadcast_volume->dbcv_devicetype == DBT_DEVTYP_VOLUME) |
39 PDEV_BROADCAST_VOLUME dev_broadcast_volume = | |
40 reinterpret_cast<PDEV_BROADCAST_VOLUME>(dev_broadcast_hdr); | |
41 return dev_broadcast_volume->dbcv_unitmask; | 109 return dev_broadcast_volume->dbcv_unitmask; |
42 } | |
43 return 0; | 110 return 0; |
44 } | 111 } |
45 | 112 |
| 113 FilePath DriveNumberToFilePath(int drive_number) { |
| 114 string16 path(L"_:\\"); |
| 115 path[0] = L'A' + drive_number; |
| 116 return FilePath(path); |
| 117 } |
| 118 |
46 } // namespace | 119 } // namespace |
47 | 120 |
48 namespace chrome { | 121 namespace chrome { |
49 | 122 |
50 RemovableDeviceNotificationsWindowWin::RemovableDeviceNotificationsWindowWin() | 123 RemovableDeviceNotificationsWindowWin::RemovableDeviceNotificationsWindowWin() |
51 : atom_(0), | 124 : window_class_(0), |
52 instance_(NULL), | 125 instance_(NULL), |
53 window_(NULL), | 126 window_(NULL), |
54 volume_name_func_(&GetVolumeName) { | 127 get_device_info_func_(&GetDeviceInfo) { |
55 Init(); | 128 DCHECK(!g_removable_device_notifications_window_win); |
56 } | 129 g_removable_device_notifications_window_win = this; |
57 | 130 } |
58 RemovableDeviceNotificationsWindowWin::RemovableDeviceNotificationsWindowWin( | 131 |
59 VolumeNameFunc volume_name_func) | 132 // static |
60 : atom_(0), | 133 RemovableDeviceNotificationsWindowWin* |
61 instance_(NULL), | 134 RemovableDeviceNotificationsWindowWin::GetInstance() { |
62 window_(NULL), | 135 DCHECK(g_removable_device_notifications_window_win); |
63 volume_name_func_(volume_name_func) { | 136 return g_removable_device_notifications_window_win; |
64 Init(); | |
65 } | 137 } |
66 | 138 |
67 void RemovableDeviceNotificationsWindowWin::Init() { | 139 void RemovableDeviceNotificationsWindowWin::Init() { |
68 WNDCLASSEX window_class; | 140 DoInit(&GetAttachedDevices); |
69 base::win::InitializeWindowClass( | 141 } |
70 WindowClassName, | 142 |
71 &base::win::WrappedWindowProc< | 143 bool RemovableDeviceNotificationsWindowWin::GetDeviceInfoForPath( |
72 RemovableDeviceNotificationsWindowWin::WndProcThunk>, | 144 const FilePath& path, |
73 0, 0, 0, NULL, NULL, NULL, NULL, NULL, | 145 base::SystemMonitor::RemovableStorageInfo* device_info) { |
74 &window_class); | 146 string16 location; |
75 instance_ = window_class.hInstance; | 147 std::string unique_id; |
76 atom_ = RegisterClassEx(&window_class); | 148 string16 name; |
77 DCHECK(atom_); | 149 bool removable; |
78 | 150 if (!get_device_info_func_(path, &location, &unique_id, &name, &removable)) |
79 window_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, 0, 0, instance_, | 151 return false; |
80 0); | 152 |
81 SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); | 153 // To compute the device id, the device type is needed. For removable |
82 } | 154 // devices, that requires knowing if there's a DCIM directory, which would |
83 | 155 // require bouncing over to the file thread. Instead, just iterate the |
84 RemovableDeviceNotificationsWindowWin::~RemovableDeviceNotificationsWindowWin( | 156 // devices in SystemMonitor. |
85 ) { | 157 std::string device_id; |
86 if (window_) | 158 if (removable) { |
87 DestroyWindow(window_); | 159 std::vector<SystemMonitor::RemovableStorageInfo> attached_devices = |
88 | 160 SystemMonitor::Get()->GetAttachedRemovableStorage(); |
89 if (atom_) | 161 bool found = false; |
90 UnregisterClass(MAKEINTATOM(atom_), instance_); | 162 for (size_t i = 0; i < attached_devices.size(); i++) { |
91 } | 163 MediaStorageUtil::Type type; |
92 | 164 std::string id; |
93 LRESULT RemovableDeviceNotificationsWindowWin::OnDeviceChange(UINT event_type, | 165 MediaStorageUtil::CrackDeviceId(attached_devices[i].device_id, &type, |
94 DWORD data) { | 166 &id); |
| 167 if (id == unique_id) { |
| 168 found = true; |
| 169 device_id = attached_devices[i].device_id; |
| 170 break; |
| 171 } |
| 172 } |
| 173 if (!found) |
| 174 return false; |
| 175 } else { |
| 176 device_id = MediaStorageUtil::MakeDeviceId( |
| 177 MediaStorageUtil::FIXED_MASS_STORAGE, unique_id); |
| 178 } |
| 179 |
| 180 if (device_info) { |
| 181 device_info->device_id = device_id; |
| 182 device_info->name = name; |
| 183 device_info->location = location; |
| 184 } |
| 185 return true; |
| 186 } |
| 187 |
| 188 void RemovableDeviceNotificationsWindowWin::InitForTest( |
| 189 GetDeviceInfoFunc get_device_info_func, |
| 190 GetAttachedDevicesFunc get_attached_devices_func) { |
| 191 get_device_info_func_ = get_device_info_func; |
| 192 DoInit(get_attached_devices_func); |
| 193 } |
| 194 |
| 195 void RemovableDeviceNotificationsWindowWin::OnDeviceChange(UINT event_type, |
| 196 LPARAM data) { |
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
96 switch (event_type) { | 198 switch (event_type) { |
97 case DBT_DEVICEARRIVAL: { | 199 case DBT_DEVICEARRIVAL: { |
98 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); | 200 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); |
99 for (int i = 0; unitmask; ++i, unitmask >>= 1) { | 201 for (int i = 0; unitmask; ++i, unitmask >>= 1) { |
100 if (unitmask & 0x01) { | 202 if (!(unitmask & 0x01)) |
101 FilePath::StringType drive(L"_:\\"); | 203 continue; |
102 drive[0] = L'A' + i; | 204 AddNewDevice(DriveNumberToFilePath(i)); |
103 WCHAR volume_name[MAX_PATH + 1]; | |
104 if ((*volume_name_func_)(drive.c_str(), volume_name, MAX_PATH + 1)) { | |
105 // TODO(kmadhusu) We need to look up a real device id as well as | |
106 // having a fall back for volume name. | |
107 std::string device_id = MediaStorageUtil::MakeDeviceId( | |
108 MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM, | |
109 base::IntToString(i)); | |
110 BrowserThread::PostTask( | |
111 BrowserThread::FILE, FROM_HERE, | |
112 base::Bind(&RemovableDeviceNotificationsWindowWin:: | |
113 CheckDeviceTypeOnFileThread, this, device_id, | |
114 FilePath::StringType(volume_name), FilePath(drive))); | |
115 } | |
116 } | |
117 } | 205 } |
118 break; | 206 break; |
119 } | 207 } |
120 case DBT_DEVICEREMOVECOMPLETE: { | 208 case DBT_DEVICEREMOVECOMPLETE: { |
121 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); | 209 DWORD unitmask = GetVolumeBitMaskFromBroadcastHeader(data); |
122 for (int i = 0; unitmask; ++i, unitmask >>= 1) { | 210 for (int i = 0; unitmask; ++i, unitmask >>= 1) { |
123 if (unitmask & 0x01) { | 211 if (!(unitmask & 0x01)) |
124 std::string device_id = MediaStorageUtil::MakeDeviceId( | 212 continue; |
125 MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM, | 213 |
126 base::IntToString(i)); | 214 FilePath device = DriveNumberToFilePath(i); |
127 SystemMonitor::Get()->ProcessRemovableStorageDetached(device_id); | 215 MountPointDeviceIdMap::const_iterator device_info = |
128 } | 216 device_ids_.find(device); |
| 217 // If the devices isn't type removable (like a CD), it won't be there. |
| 218 if (device_info == device_ids_.end()) |
| 219 continue; |
| 220 |
| 221 SystemMonitor::Get()->ProcessRemovableStorageDetached( |
| 222 device_info->second); |
| 223 device_ids_.erase(device_info); |
129 } | 224 } |
130 break; | 225 break; |
131 } | 226 } |
132 } | 227 } |
133 return TRUE; | 228 } |
134 } | 229 |
135 | 230 RemovableDeviceNotificationsWindowWin:: |
136 void RemovableDeviceNotificationsWindowWin::CheckDeviceTypeOnFileThread( | 231 ~RemovableDeviceNotificationsWindowWin() { |
137 const std::string& id, | 232 if (window_) |
138 const FilePath::StringType& device_name, | 233 DestroyWindow(window_); |
139 const FilePath& path) { | 234 |
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 235 if (window_class_) |
141 if (!IsMediaDevice(path.value())) | 236 UnregisterClass(MAKEINTATOM(window_class_), instance_); |
142 return; | 237 |
143 | 238 DCHECK_EQ(this, g_removable_device_notifications_window_win); |
144 BrowserThread::PostTask( | 239 g_removable_device_notifications_window_win = NULL; |
145 BrowserThread::UI, FROM_HERE, | 240 } |
146 base::Bind( | 241 |
147 &RemovableDeviceNotificationsWindowWin:: | 242 // static |
148 ProcessRemovableDeviceAttachedOnUIThread, | 243 LRESULT CALLBACK RemovableDeviceNotificationsWindowWin::WndProcThunk( |
149 this, id, device_name, path)); | 244 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { |
150 } | 245 RemovableDeviceNotificationsWindowWin* msg_wnd = |
151 | 246 reinterpret_cast<RemovableDeviceNotificationsWindowWin*>( |
152 void | 247 GetWindowLongPtr(hwnd, GWLP_USERDATA)); |
153 RemovableDeviceNotificationsWindowWin::ProcessRemovableDeviceAttachedOnUIThread( | 248 if (msg_wnd) |
154 const std::string& id, | 249 return msg_wnd->WndProc(hwnd, message, wparam, lparam); |
155 const FilePath::StringType& device_name, | 250 return ::DefWindowProc(hwnd, message, wparam, lparam); |
156 const FilePath& path) { | |
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
158 | |
159 // TODO(kmadhusu) Record device info histogram. | |
160 SystemMonitor::Get()->ProcessRemovableStorageAttached(id, | |
161 device_name, | |
162 path.value()); | |
163 } | 251 } |
164 | 252 |
165 LRESULT CALLBACK RemovableDeviceNotificationsWindowWin::WndProc( | 253 LRESULT CALLBACK RemovableDeviceNotificationsWindowWin::WndProc( |
166 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | 254 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { |
167 switch (message) { | 255 switch (message) { |
168 case WM_DEVICECHANGE: | 256 case WM_DEVICECHANGE: |
169 return OnDeviceChange(static_cast<UINT>(wparam), | 257 OnDeviceChange(static_cast<UINT>(wparam), lparam); |
170 static_cast<DWORD>(lparam)); | 258 return TRUE; |
171 default: | 259 default: |
172 break; | 260 break; |
173 } | 261 } |
174 | 262 |
175 return ::DefWindowProc(hwnd, message, wparam, lparam); | 263 return ::DefWindowProc(hwnd, message, wparam, lparam); |
176 } | 264 } |
177 | 265 |
178 // static | 266 void RemovableDeviceNotificationsWindowWin::DoInit( |
179 LRESULT CALLBACK RemovableDeviceNotificationsWindowWin::WndProcThunk( | 267 GetAttachedDevicesFunc get_attached_devices_func) { |
180 HWND hwnd, | 268 WNDCLASSEX window_class; |
181 UINT message, | 269 base::win::InitializeWindowClass( |
182 WPARAM wparam, | 270 kWindowClassName, |
183 LPARAM lparam) { | 271 &WrappedWindowProc<RemovableDeviceNotificationsWindowWin::WndProcThunk>, |
184 RemovableDeviceNotificationsWindowWin* msg_wnd = | 272 0, 0, 0, NULL, NULL, NULL, NULL, NULL, |
185 reinterpret_cast<RemovableDeviceNotificationsWindowWin*>( | 273 &window_class); |
186 GetWindowLongPtr(hwnd, GWLP_USERDATA)); | 274 instance_ = window_class.hInstance; |
187 if (msg_wnd) | 275 window_class_ = RegisterClassEx(&window_class); |
188 return msg_wnd->WndProc(hwnd, message, wparam, lparam); | 276 DCHECK(window_class_); |
189 return ::DefWindowProc(hwnd, message, wparam, lparam); | 277 |
| 278 window_ = CreateWindow(MAKEINTATOM(window_class_), 0, 0, 0, 0, 0, 0, 0, 0, |
| 279 instance_, 0); |
| 280 SetWindowLongPtr(window_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); |
| 281 |
| 282 std::vector<FilePath> removable_devices = get_attached_devices_func(); |
| 283 for (size_t i = 0; i < removable_devices.size(); i++) |
| 284 AddNewDevice(removable_devices[i]); |
| 285 } |
| 286 |
| 287 void RemovableDeviceNotificationsWindowWin::AddNewDevice( |
| 288 const FilePath& device_path) { |
| 289 std::string unique_id; |
| 290 string16 device_name; |
| 291 bool removable; |
| 292 if (!get_device_info_func_(device_path, NULL, &unique_id, &device_name, |
| 293 &removable)) { |
| 294 return; |
| 295 } |
| 296 |
| 297 if (!removable) |
| 298 return; |
| 299 |
| 300 BrowserThread::PostTask( |
| 301 BrowserThread::FILE, |
| 302 FROM_HERE, |
| 303 base::Bind( |
| 304 &RemovableDeviceNotificationsWindowWin::CheckDeviceTypeOnFileThread, |
| 305 this, unique_id, device_name, device_path)); |
| 306 } |
| 307 |
| 308 void RemovableDeviceNotificationsWindowWin::CheckDeviceTypeOnFileThread( |
| 309 const std::string& unique_id, |
| 310 const FilePath::StringType& device_name, |
| 311 const FilePath& device) { |
| 312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 313 |
| 314 MediaStorageUtil::Type type = |
| 315 MediaStorageUtil::REMOVABLE_MASS_STORAGE_NO_DCIM; |
| 316 if (IsMediaDevice(device.value())) |
| 317 type = MediaStorageUtil::REMOVABLE_MASS_STORAGE_WITH_DCIM; |
| 318 std::string device_id = MediaStorageUtil::MakeDeviceId(type, unique_id); |
| 319 |
| 320 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 321 &RemovableDeviceNotificationsWindowWin::ProcessDeviceAttachedOnUIThread, |
| 322 this, device_id, device_name, device)); |
| 323 } |
| 324 |
| 325 void RemovableDeviceNotificationsWindowWin::ProcessDeviceAttachedOnUIThread( |
| 326 const std::string& device_id, |
| 327 const FilePath::StringType& device_name, |
| 328 const FilePath& device) { |
| 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 330 |
| 331 // TODO(kmadhusu) Record device info histogram. |
| 332 device_ids_[device] = device_id; |
| 333 SystemMonitor::Get()->ProcessRemovableStorageAttached(device_id, |
| 334 device_name, |
| 335 device.value()); |
190 } | 336 } |
191 | 337 |
192 } // namespace chrome | 338 } // namespace chrome |
OLD | NEW |