Chromium Code Reviews| Index: chrome/browser/chromeos/power/peripheral_battery_observer.cc |
| diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer.cc b/chrome/browser/chromeos/power/peripheral_battery_observer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2865f556cb875441275903092b60faf9eb284b19 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/power/peripheral_battery_observer.cc |
| @@ -0,0 +1,220 @@ |
| +// Copyright (c) 2013 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 <vector> |
| + |
| +#include "chrome/browser/chromeos/power/peripheral_battery_observer.h" |
| + |
| +#include "ash/shell.h" |
| +#include "base/bind.h" |
| +#include "base/string16.h" |
| +#include "base/stringprintf.h" |
| +#include "base/strings/string_split.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/browser/notifications/notification.h" |
| +#include "chrome/browser/notifications/notification_ui_manager.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| +#include "chromeos/dbus/dbus_thread_manager.h" |
| +#include "device/bluetooth/bluetooth_adapter_factory.h" |
| +#include "device/bluetooth/bluetooth_device.h" |
| +#include "grit/ash_strings.h" |
| +#include "grit/theme_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/image/image.h" |
| + |
| +namespace chromeos { |
| + |
| +namespace { |
| + |
| +// When a peripheral device's battery level is <= kLowBatteryLevel, consider |
| +// it to be in low battery condition. |
| +const int kLowBatteryLevel = 15; |
|
Daniel Erat
2013/04/09 02:06:18
nit: add blank line after this
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| +// Don't show 2 low battery notification within |kNotificationIntervalSec| |
| +// seconds. |
| +const int kNotificationIntervalSec = 60; |
|
Daniel Erat
2013/04/09 02:06:18
nit: add blank line after this
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| +// HID Bluetooth device's battery sysfs entry path looks like |
| +// "/sys/class/power_supply/hid-AA:BB:CC:DD:EE:FF-battery". |
| +// Here the bluetooth address is showed in reverse order and its true |
| +// address "FF:EE:DD:CC:BB:AA". |
| +const char kHIDBatteryPathHeader[] = "/sys/class/power_supply/hid-"; |
|
Daniel Erat
2013/04/09 02:06:18
instead of "header" and "end", how about "prefix"
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| +const char kHIDBatteryPathEnd[] = "-battery"; |
| + |
| +class PeripheralBatteryNotificationDelegate : public NotificationDelegate { |
| + public: |
| + explicit PeripheralBatteryNotificationDelegate(const std::string& id) |
| + : id_(id) {} |
| + |
| + // Overridden from NotificationDelegate: |
| + virtual void Display() OVERRIDE {} |
| + virtual void Error() OVERRIDE {} |
| + virtual void Close(bool by_user) OVERRIDE {} |
| + virtual void Click() OVERRIDE {} |
| + virtual std::string id() const OVERRIDE { return id_; } |
| + // A NULL return value prevents loading image from URL. It is OK since our |
| + // implementation loads image from system resource bundle. |
| + virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { |
| + return NULL; |
| + } |
| + |
| + private: |
| + const std::string id_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate); |
| +}; |
| + |
| +} // namespace |
| + |
| +PeripheralBatteryObserver::PeripheralBatteryObserver() |
| + : weakptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST( |
| + new base::WeakPtrFactory<PeripheralBatteryObserver>(this))) { |
| + DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this); |
| + device::BluetoothAdapterFactory::GetAdapter( |
| + base::Bind(&PeripheralBatteryObserver::InitializeOnBluetoothReady, |
| + weakptr_factory_->GetWeakPtr())); |
| +} |
| + |
| +PeripheralBatteryObserver::~PeripheralBatteryObserver() { |
| + if (bluetooth_adapter_.get()) |
| + bluetooth_adapter_->RemoveObserver(this); |
| + DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this); |
| +} |
| + |
| +void PeripheralBatteryObserver::InitializeOnBluetoothReady( |
| + scoped_refptr<device::BluetoothAdapter> adapter) { |
| + bluetooth_adapter_ = adapter; |
| + CHECK(bluetooth_adapter_); |
| + bluetooth_adapter_->AddObserver(this); |
| +} |
| + |
| +void PeripheralBatteryObserver::PeripheralBatteryStatusReceived( |
| + const std::string& path, |
| + const std::string& name, |
| + int level) { |
| + std::string key; |
|
Daniel Erat
2013/04/09 02:06:18
rename this variable to "address" to make it clear
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + if (IsBluetoothHIDBattery(path)) { |
| + // For HID bluetooh device, device address is used as key to index |
| + // BatteryInfo. |
| + key = ExtractBluetoothAddress(path); |
| + } else { |
| + LOG(ERROR) << "Unsupported battery path " << path; |
| + return; |
| + } |
| + |
| + if (level < -1 || level > 100) { |
| + LOG(ERROR) << "Invalid battery level " << level |
| + << " for device " << name << " at path " << path; |
| + return; |
| + } |
| + // If unknown battery level received, cancel any existing notification. |
| + if (level == -1) { |
| + CancelNotification(key); |
| + return; |
| + } |
| + |
| + // Post the notification in 2 cases: |
| + // 1. It's the first time the battery level is received, and it is |
| + // below kLowBatteryLevel. |
| + // 2. The battery level is in record and it drops below kLowBatteryLevel. |
| + if (batteries_.find(key) == batteries_.end()) { |
| + BatteryInfo battery(name, level, base::TimeTicks(), base::TimeTicks::Now()); |
| + if (level <= kLowBatteryLevel) { |
| + if (PostNotification(key, battery)) |
| + battery.set_notification_timestamp(base::TimeTicks::Now()); |
| + } |
| + batteries_[key] = battery; |
| + } else { |
| + BatteryInfo* battery = &batteries_[key]; |
| + battery->set_name(name); |
| + int old_level = battery->level(); |
| + battery->set_level(level); |
| + battery->set_update_timestamp(base::TimeTicks::Now()); |
| + if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) { |
| + if (PostNotification(key, *battery)) |
| + battery->set_notification_timestamp(base::TimeTicks::Now()); |
| + } |
| + } |
| +} |
| + |
| +void PeripheralBatteryObserver::DeviceChanged(device::BluetoothAdapter* adapter, |
| + device::BluetoothDevice* device) { |
| + if (!device->IsPaired()) { |
|
Daniel Erat
2013/04/09 02:06:18
nit: remove curly braces since the statement is a
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + RemoveBattery(device->GetAddress()); |
| + } |
| +} |
| + |
| +void PeripheralBatteryObserver::DeviceRemoved(device::BluetoothAdapter* adapter, |
| + device::BluetoothDevice* device) { |
| + RemoveBattery(device->GetAddress()); |
| +} |
| + |
| +bool PeripheralBatteryObserver::IsBluetoothHIDBattery(const std::string& path) { |
| + return StartsWithASCII(path, kHIDBatteryPathHeader, false) && |
| + EndsWith(path, kHIDBatteryPathEnd, false); |
| +} |
| + |
| +std::string PeripheralBatteryObserver::ExtractBluetoothAddress( |
| + const std::string& path) { |
| + int header_size = sizeof(kHIDBatteryPathHeader); |
|
Daniel Erat
2013/04/09 02:06:18
indent this line and the following ones two spaces
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + int end_size = sizeof(kHIDBatteryPathEnd); |
|
Daniel Erat
2013/04/09 02:06:18
this makes me nervous. will it break if someone c
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + int key_len = path.size() - (header_size - 1) - (end_size - 1); |
|
Daniel Erat
2013/04/09 02:06:18
this makes me nervous. return early if key_len <=
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + std::string reverse_address = path.substr(header_size - 1, key_len); |
| + StringToLowerASCII(&reverse_address); |
| + std::vector<std::string> result; |
| + base::SplitString(reverse_address, ':', &result); |
| + std::reverse(result.begin(), result.end()); |
| + std::string address = JoinString(result, ':'); |
| + return address; |
| +} |
| + |
| +void PeripheralBatteryObserver::RemoveBattery(const std::string& key) { |
| + std::string key_lowercase = key; |
| + StringToLowerASCII(&key_lowercase); |
| + if (batteries_.find(key_lowercase) != batteries_.end()) { |
|
Daniel Erat
2013/04/09 02:06:18
if you aren't going to save the iterator to make t
Yufeng Shen (Slow to review)
2013/04/10 05:54:08
Done.
|
| + batteries_.erase(key_lowercase); |
| + CancelNotification(key_lowercase); |
| + } |
| +} |
| + |
| +bool PeripheralBatteryObserver::PostNotification(const std::string& id, |
| + const BatteryInfo& battery) { |
| + // Only post notification if kNotificationInterval seconds have passed since |
| + // last notification showed, avoiding the case where the battery level |
| + // oscillates around the threshold level. |
| + if (base::TimeTicks::Now() - battery.last_notification_timestamp() < |
| + base::TimeDelta::FromSeconds(kNotificationIntervalSec)) |
| + return false; |
| + |
| + NotificationUIManager* notification_manager = |
| + g_browser_process->notification_ui_manager(); |
| + |
| + base::string16 string_text = l10n_util::GetStringFUTF16Int( |
| + IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT, |
| + battery.level()); |
| + |
| + Notification notification( |
| + // TODO(mukai): add SYSTEM priority and use here. |
| + GURL(), |
| + ui::ResourceBundle::GetSharedInstance().GetImageNamed( |
| + IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW), |
| + UTF8ToUTF16(battery.name()), |
| + string_text, |
| + WebKit::WebTextDirectionDefault, |
| + string16(), |
| + UTF8ToUTF16(id), |
| + new PeripheralBatteryNotificationDelegate(id)); |
| + |
| + notification_manager->Add(notification, |
| + ProfileManager::GetDefaultProfileOrOffTheRecord()); |
| + |
| + return true; |
| +} |
| + |
| +void PeripheralBatteryObserver::CancelNotification(const std::string& id) { |
| + g_browser_process->notification_ui_manager()->CancelById(id); |
| +} |
| + |
| + |
| +} // namespace chromeos |