blob: 0c8175159577947b7859a253c6bc76dc87eff720 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/core/device_monitor_win.h"
#include <dbt.h>
#include <map>
#include <windows.h>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/win/message_window.h"
namespace device {
class DeviceMonitorMessageWindow;
namespace {
const wchar_t kWindowClassName[] = L"DeviceMonitorMessageWindow";
DeviceMonitorMessageWindow* g_message_window;
// Provides basic comparability for GUIDs so that they can be used as keys to an
// STL map.
struct CompareGUID {
bool operator()(const GUID& a, const GUID& b) {
return memcmp(&a, &b, sizeof a) < 0;
}
};
}
// This singleton class manages a shared message window for all registered
// device notification observers. It vends one instance of DeviceManagerWin for
// each unique GUID it sees.
class DeviceMonitorMessageWindow {
public:
static DeviceMonitorMessageWindow* GetInstance() {
if (!g_message_window) {
g_message_window = new DeviceMonitorMessageWindow();
base::AtExitManager::RegisterTask(
base::Bind(&base::DeletePointer<DeviceMonitorMessageWindow>,
base::Unretained(g_message_window)));
}
return g_message_window;
}
DeviceMonitorWin* GetForDeviceInterface(const GUID& device_interface) {
scoped_ptr<DeviceMonitorWin>& device_monitor =
device_monitors_[device_interface];
if (!device_monitor) {
DEV_BROADCAST_DEVICEINTERFACE db = {sizeof(DEV_BROADCAST_DEVICEINTERFACE),
DBT_DEVTYP_DEVICEINTERFACE,
0,
device_interface};
HDEVNOTIFY notify_handle = RegisterDeviceNotification(
window_->hwnd(), &db, DEVICE_NOTIFY_WINDOW_HANDLE);
if (!notify_handle) {
LOG(ERROR) << "Failed to register for device notifications.";
return nullptr;
}
device_monitor.reset(new DeviceMonitorWin(notify_handle));
}
return device_monitor.get();
}
private:
friend void base::DeletePointer<DeviceMonitorMessageWindow>(
DeviceMonitorMessageWindow* message_window);
DeviceMonitorMessageWindow() {
window_.reset(new base::win::MessageWindow());
if (!window_->CreateNamed(
base::Bind(&DeviceMonitorMessageWindow::HandleMessage,
base::Unretained(this)),
base::string16(kWindowClassName))) {
LOG(ERROR) << "Failed to create message window: " << kWindowClassName;
window_.reset();
}
}
~DeviceMonitorMessageWindow() {}
bool HandleMessage(UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (message == WM_DEVICECHANGE) {
DEV_BROADCAST_DEVICEINTERFACE* db =
reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lparam);
const auto& map_entry = device_monitors_.find(db->dbcc_classguid);
if (map_entry == device_monitors_.end()) {
return false;
}
DeviceMonitorWin* device_monitor = map_entry->second.get();
std::string device_path(base::SysWideToUTF8(db->dbcc_name));
DCHECK(base::IsStringASCII(device_path));
if (wparam == DBT_DEVICEARRIVAL) {
device_monitor->NotifyDeviceAdded(
base::StringToLowerASCII(device_path));
} else if (wparam == DBT_DEVICEREMOVECOMPLETE) {
device_monitor->NotifyDeviceRemoved(
base::StringToLowerASCII(device_path));
} else {
return false;
}
*result = NULL;
return true;
}
return false;
}
std::map<GUID, scoped_ptr<DeviceMonitorWin>, CompareGUID> device_monitors_;
scoped_ptr<base::win::MessageWindow> window_;
DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMessageWindow);
};
void DeviceMonitorWin::Observer::OnDeviceAdded(const std::string& device_path) {
}
void DeviceMonitorWin::Observer::OnDeviceRemoved(
const std::string& device_path) {
}
// static
DeviceMonitorWin* DeviceMonitorWin::GetForDeviceInterface(
const GUID& device_interface) {
DeviceMonitorMessageWindow* message_window =
DeviceMonitorMessageWindow::GetInstance();
return message_window->GetForDeviceInterface(device_interface);
}
DeviceMonitorWin::~DeviceMonitorWin() {
UnregisterDeviceNotification(notify_handle_);
}
void DeviceMonitorWin::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void DeviceMonitorWin::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
DeviceMonitorWin::DeviceMonitorWin(HDEVNOTIFY notify_handle)
: notify_handle_(notify_handle) {
}
void DeviceMonitorWin::NotifyDeviceAdded(const std::string& device_path) {
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device_path));
}
void DeviceMonitorWin::NotifyDeviceRemoved(const std::string& device_path) {
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device_path));
}
}