| // 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/battery/battery_status_manager_win.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram.h" |
| #include "base/strings/string16.h" |
| #include "base/win/message_window.h" |
| #include "base/win/windows_version.h" |
| #include "device/battery/battery_status_manager.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback; |
| |
| const wchar_t kWindowClassName[] = L"BatteryStatusMessageWindow"; |
| |
| // This enum is used for histogram. Don't change the order of the existing |
| // values. |
| enum NumberBatteriesType { |
| UNKNOWN_BATTERIES = 0, |
| NO_BATTERY = 1, |
| ONE_OR_MORE_BATTERIES = 2, |
| BATTERY_TYPES_COUNT = 3, |
| }; |
| |
| void UpdateNumberBatteriesHistogram(NumberBatteriesType count) { |
| UMA_HISTOGRAM_ENUMERATION("BatteryStatus.NumberBatteriesWin", |
| count, |
| BATTERY_TYPES_COUNT); |
| } |
| |
| void UpdateNumberBatteriesHistogram() { |
| SYSTEM_POWER_STATUS win_status; |
| if (!GetSystemPowerStatus(&win_status)) { |
| UpdateNumberBatteriesHistogram(UNKNOWN_BATTERIES); |
| return; |
| } |
| |
| if (win_status.BatteryFlag == 255) |
| UpdateNumberBatteriesHistogram(UNKNOWN_BATTERIES); |
| else if (win_status.BatteryFlag == 128) |
| UpdateNumberBatteriesHistogram(NO_BATTERY); |
| else |
| UpdateNumberBatteriesHistogram(ONE_OR_MORE_BATTERIES); |
| } |
| |
| // Message-only window for handling battery changes on Windows. |
| class BatteryStatusObserver { |
| public: |
| explicit BatteryStatusObserver(const BatteryCallback& callback) |
| : power_handle_(NULL), |
| battery_change_handle_(NULL), |
| callback_(callback) { |
| } |
| |
| ~BatteryStatusObserver() { DCHECK(!window_); } |
| |
| void Start() { |
| if (CreateMessageWindow()) { |
| BatteryChanged(); |
| // RegisterPowerSettingNotification function work from Windows Vista |
| // onwards. However even without them we will receive notifications, |
| // e.g. when a power source is connected. |
| // TODO(timvolodine) : consider polling for battery changes on windows |
| // versions prior to Vista, see crbug.com/402466. |
| power_handle_ = |
| RegisterNotification(&GUID_ACDC_POWER_SOURCE); |
| battery_change_handle_ = |
| RegisterNotification(&GUID_BATTERY_PERCENTAGE_REMAINING); |
| } else { |
| // Could not create a message window, execute callback with the default |
| // values. |
| callback_.Run(BatteryStatus()); |
| } |
| |
| UpdateNumberBatteriesHistogram(); |
| } |
| |
| void Stop() { |
| if (power_handle_) { |
| UnregisterNotification(power_handle_); |
| power_handle_ = NULL; |
| } |
| if (battery_change_handle_) { |
| UnregisterNotification(battery_change_handle_); |
| battery_change_handle_ = NULL; |
| } |
| window_.reset(); |
| } |
| |
| private: |
| void BatteryChanged() { |
| SYSTEM_POWER_STATUS win_status; |
| if (GetSystemPowerStatus(&win_status)) |
| callback_.Run(ComputeWebBatteryStatus(win_status)); |
| else |
| callback_.Run(BatteryStatus()); |
| } |
| |
| bool HandleMessage(UINT message, |
| WPARAM wparam, |
| LPARAM lparam, |
| LRESULT* result) { |
| switch(message) { |
| case WM_POWERBROADCAST: |
| if (wparam == PBT_APMPOWERSTATUSCHANGE || |
| wparam == PBT_POWERSETTINGCHANGE) { |
| BatteryChanged(); |
| } |
| *result = NULL; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| HPOWERNOTIFY RegisterNotification(LPCGUID power_setting) { |
| if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| return NULL; |
| |
| return RegisterPowerSettingNotification(window_->hwnd(), power_setting, |
| DEVICE_NOTIFY_WINDOW_HANDLE); |
| } |
| |
| BOOL UnregisterNotification(HPOWERNOTIFY handle) { |
| if (base::win::GetVersion() < base::win::VERSION_VISTA) |
| return FALSE; |
| |
| return UnregisterPowerSettingNotification(handle); |
| } |
| |
| bool CreateMessageWindow() { |
| // TODO(timvolodine): consider reusing the message window of PowerMonitor. |
| window_.reset(new base::win::MessageWindow()); |
| if (!window_->CreateNamed(base::Bind(&BatteryStatusObserver::HandleMessage, |
| base::Unretained(this)), |
| base::string16(kWindowClassName))) { |
| LOG(ERROR) << "Failed to create message window: " << kWindowClassName; |
| window_.reset(); |
| return false; |
| } |
| return true; |
| } |
| |
| HPOWERNOTIFY power_handle_; |
| HPOWERNOTIFY battery_change_handle_; |
| BatteryCallback callback_; |
| std::unique_ptr<base::win::MessageWindow> window_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); |
| }; |
| |
| class BatteryStatusManagerWin : public BatteryStatusManager { |
| public: |
| explicit BatteryStatusManagerWin(const BatteryCallback& callback) |
| : battery_observer_(new BatteryStatusObserver(callback)) {} |
| ~BatteryStatusManagerWin() override { battery_observer_->Stop(); } |
| |
| public: |
| // BatteryStatusManager: |
| bool StartListeningBatteryChange() override { |
| battery_observer_->Start(); |
| return true; |
| } |
| |
| void StopListeningBatteryChange() override { battery_observer_->Stop(); } |
| |
| private: |
| std::unique_ptr<BatteryStatusObserver> battery_observer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerWin); |
| }; |
| |
| } // namespace |
| |
| BatteryStatus ComputeWebBatteryStatus(const SYSTEM_POWER_STATUS& win_status) { |
| BatteryStatus status; |
| status.charging = win_status.ACLineStatus != WIN_AC_LINE_STATUS_OFFLINE; |
| |
| // Set level if available. Otherwise keep the default value which is 1. |
| if (win_status.BatteryLifePercent != 255) { |
| // Convert percentage to a value between 0 and 1 with 2 significant digits. |
| status.level = static_cast<double>(win_status.BatteryLifePercent) / 100.; |
| } |
| |
| if (!status.charging) { |
| // Set discharging_time if available otherwise keep the default value, |
| // which is +Infinity. |
| if (win_status.BatteryLifeTime != (DWORD)-1) |
| status.discharging_time = win_status.BatteryLifeTime; |
| status.charging_time = std::numeric_limits<double>::infinity(); |
| } else { |
| // Set charging_time to +Infinity if not fully charged, otherwise leave the |
| // default value, which is 0. |
| if (status.level < 1) |
| status.charging_time = std::numeric_limits<double>::infinity(); |
| } |
| return status; |
| } |
| |
| // static |
| std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create( |
| const BatteryStatusService::BatteryUpdateCallback& callback) { |
| return std::unique_ptr<BatteryStatusManager>( |
| new BatteryStatusManagerWin(callback)); |
| } |
| |
| } // namespace device |