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