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 "device/base/device_monitor_win.h"
6
7 // windows.h must be included before dbt.h.
8 #include <windows.h>
9
10 #include <dbt.h>
11
12 #include <map>
13 #include <memory>
14
15 #include "base/at_exit.h"
16 #include "base/bind.h"
17 #include "base/bind_helpers.h"
18 #include "base/macros.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/win/message_window.h"
22
23 namespace device {
24
25 class DeviceMonitorMessageWindow;
26
27 namespace {
28
29 const base::char16 kWindowClassName[] =
30 STRING16_LITERAL("DeviceMonitorMessageWindow");
31 DeviceMonitorMessageWindow* g_message_window;
32
33 // Provides basic comparability for GUIDs so that they can be used as keys to an
34 // STL map.
35 struct CompareGUID {
operator ()device::__anon456f10e10111::CompareGUID36 bool operator()(const GUID& a, const GUID& b) const {
37 return memcmp(&a, &b, sizeof a) < 0;
38 }
39 };
40 }
41
42 // This singleton class manages a shared message window for all registered
43 // device notification observers. It vends one instance of DeviceManagerWin for
44 // each unique GUID it sees.
45 class DeviceMonitorMessageWindow {
46 public:
GetInstance()47 static DeviceMonitorMessageWindow* GetInstance() {
48 if (!g_message_window) {
49 g_message_window = new DeviceMonitorMessageWindow();
50 if (g_message_window->Init()) {
51 base::AtExitManager::RegisterTask(
52 base::BindOnce(&base::DeletePointer<DeviceMonitorMessageWindow>,
53 base::Unretained(g_message_window)));
54 } else {
55 delete g_message_window;
56 g_message_window = nullptr;
57 }
58 }
59 return g_message_window;
60 }
61
GetForDeviceInterface(const GUID & device_interface)62 DeviceMonitorWin* GetForDeviceInterface(const GUID& device_interface) {
63 std::unique_ptr<DeviceMonitorWin>& device_monitor =
64 device_monitors_[device_interface];
65 if (!device_monitor) {
66 device_monitor.reset(new DeviceMonitorWin());
67 }
68 return device_monitor.get();
69 }
70
GetForAllInterfaces()71 DeviceMonitorWin* GetForAllInterfaces() { return &all_device_monitor_; }
72
73 private:
74 friend void base::DeletePointer<DeviceMonitorMessageWindow>(
75 DeviceMonitorMessageWindow* message_window);
76
DeviceMonitorMessageWindow()77 DeviceMonitorMessageWindow() {}
78
~DeviceMonitorMessageWindow()79 ~DeviceMonitorMessageWindow() {
80 if (notify_handle_) {
81 UnregisterDeviceNotification(notify_handle_);
82 }
83 }
84
Init()85 bool Init() {
86 window_.reset(new base::win::MessageWindow());
87 if (!window_->CreateNamed(
88 base::BindRepeating(&DeviceMonitorMessageWindow::HandleMessage,
89 base::Unretained(this)),
90 base::string16(kWindowClassName))) {
91 LOG(ERROR) << "Failed to create message window: " << kWindowClassName;
92 return false;
93 }
94
95 DEV_BROADCAST_DEVICEINTERFACE db = {sizeof(DEV_BROADCAST_DEVICEINTERFACE),
96 DBT_DEVTYP_DEVICEINTERFACE};
97 notify_handle_ = RegisterDeviceNotification(
98 window_->hwnd(), &db,
99 DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
100 if (!notify_handle_) {
101 PLOG(ERROR) << "Failed to register for device notifications";
102 return false;
103 }
104
105 return true;
106 }
107
HandleMessage(UINT message,WPARAM wparam,LPARAM lparam,LRESULT * result)108 bool HandleMessage(UINT message,
109 WPARAM wparam,
110 LPARAM lparam,
111 LRESULT* result) {
112 if (message == WM_DEVICECHANGE &&
113 (wparam == DBT_DEVICEARRIVAL || wparam == DBT_DEVICEREMOVECOMPLETE)) {
114 DEV_BROADCAST_HDR* hdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lparam);
115 if (hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
116 return false;
117
118 DEV_BROADCAST_DEVICEINTERFACE* db =
119 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(hdr);
120
121 DeviceMonitorWin* device_monitor = nullptr;
122 const auto& map_entry = device_monitors_.find(db->dbcc_classguid);
123 if (map_entry != device_monitors_.end())
124 device_monitor = map_entry->second.get();
125
126 base::string16 device_path(db->dbcc_name);
127 DCHECK(base::IsStringASCII(device_path));
128 device_path = base::ToLowerASCII(device_path);
129
130 if (wparam == DBT_DEVICEARRIVAL) {
131 if (device_monitor) {
132 device_monitor->NotifyDeviceAdded(db->dbcc_classguid, device_path);
133 }
134 all_device_monitor_.NotifyDeviceAdded(db->dbcc_classguid, device_path);
135 } else if (wparam == DBT_DEVICEREMOVECOMPLETE) {
136 if (device_monitor) {
137 device_monitor->NotifyDeviceRemoved(db->dbcc_classguid, device_path);
138 }
139 all_device_monitor_.NotifyDeviceRemoved(db->dbcc_classguid,
140 device_path);
141 }
142 *result = NULL;
143 return true;
144 }
145 return false;
146 }
147
148 std::map<GUID, std::unique_ptr<DeviceMonitorWin>, CompareGUID>
149 device_monitors_;
150 DeviceMonitorWin all_device_monitor_;
151 std::unique_ptr<base::win::MessageWindow> window_;
152 HDEVNOTIFY notify_handle_ = NULL;
153
154 DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMessageWindow);
155 };
156
OnDeviceAdded(const GUID & class_guid,const base::string16 & device_path)157 void DeviceMonitorWin::Observer::OnDeviceAdded(
158 const GUID& class_guid,
159 const base::string16& device_path) {}
160
OnDeviceRemoved(const GUID & class_guid,const base::string16 & device_path)161 void DeviceMonitorWin::Observer::OnDeviceRemoved(
162 const GUID& class_guid,
163 const base::string16& device_path) {}
164
165 // static
GetForDeviceInterface(const GUID & device_interface)166 DeviceMonitorWin* DeviceMonitorWin::GetForDeviceInterface(
167 const GUID& device_interface) {
168 DeviceMonitorMessageWindow* message_window =
169 DeviceMonitorMessageWindow::GetInstance();
170 if (message_window) {
171 return message_window->GetForDeviceInterface(device_interface);
172 }
173 return nullptr;
174 }
175
176 // static
GetForAllInterfaces()177 DeviceMonitorWin* DeviceMonitorWin::GetForAllInterfaces() {
178 DeviceMonitorMessageWindow* message_window =
179 DeviceMonitorMessageWindow::GetInstance();
180 if (message_window) {
181 return message_window->GetForAllInterfaces();
182 }
183 return nullptr;
184 }
185
~DeviceMonitorWin()186 DeviceMonitorWin::~DeviceMonitorWin() {}
187
AddObserver(Observer * observer)188 void DeviceMonitorWin::AddObserver(Observer* observer) {
189 observer_list_.AddObserver(observer);
190 }
191
RemoveObserver(Observer * observer)192 void DeviceMonitorWin::RemoveObserver(Observer* observer) {
193 observer_list_.RemoveObserver(observer);
194 }
195
DeviceMonitorWin()196 DeviceMonitorWin::DeviceMonitorWin() {}
197
NotifyDeviceAdded(const GUID & class_guid,const base::string16 & device_path)198 void DeviceMonitorWin::NotifyDeviceAdded(const GUID& class_guid,
199 const base::string16& device_path) {
200 for (auto& observer : observer_list_)
201 observer.OnDeviceAdded(class_guid, device_path);
202 }
203
NotifyDeviceRemoved(const GUID & class_guid,const base::string16 & device_path)204 void DeviceMonitorWin::NotifyDeviceRemoved(const GUID& class_guid,
205 const base::string16& device_path) {
206 for (auto& observer : observer_list_)
207 observer.OnDeviceRemoved(class_guid, device_path);
208 }
209
210 } // namespace device
211