Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3598)

Unified Diff: chrome/browser/chromeos/power/peripheral_battery_observer.cc

Issue 13638018: Add PeripheralBatteryObserver (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: make a fake kNotificationOriginUrl to make origin check in notification manager happy Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..f2487bc5a3688d624d094aa0ad36bfec70303df9
--- /dev/null
+++ b/chrome/browser/chromeos/power/peripheral_battery_observer.cc
@@ -0,0 +1,239 @@
+// 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 "chrome/browser/chromeos/power/peripheral_battery_observer.h"
+
+#include <vector>
+
+#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 "content/public/browser/browser_thread.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;
+
+// Don't show 2 low battery notification within |kNotificationIntervalSec|
+// seconds.
+const int kNotificationIntervalSec = 60;
+
+const char kNotificationOriginUrl[] = "chrome://peripheral-battery";
+
+// 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 kHIDBatteryPathPrefix[] = "/sys/class/power_supply/hid-";
+const char kHIDBatteryPathSuffix[] = "-battery";
+
+bool IsBluetoothHIDBattery(const std::string& path) {
+ return StartsWithASCII(path, kHIDBatteryPathPrefix, false) &&
+ EndsWith(path, kHIDBatteryPathSuffix, false);
+}
+
+std::string ExtractBluetoothAddress(const std::string& path) {
+ int header_size = strlen(kHIDBatteryPathPrefix);
+ int end_size = strlen(kHIDBatteryPathSuffix);
+ int key_len = path.size() - header_size - end_size;
+ if (key_len <= 0)
+ return std::string();
+ std::string reverse_address = path.substr(header_size, 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;
+}
+
+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:
+ virtual ~PeripheralBatteryNotificationDelegate() {}
+
+ const std::string id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate);
+};
+
+} // namespace
+
+PeripheralBatteryObserver::PeripheralBatteryObserver()
+ : testing_clock_(NULL),
+ 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::PeripheralBatteryStatusReceived(
+ const std::string& path,
+ const std::string& name,
+ int level) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ std::string address;
+ if (IsBluetoothHIDBattery(path)) {
+ // For HID bluetooth device, device address is used as key to index
+ // BatteryInfo.
+ address = ExtractBluetoothAddress(path);
+ } else {
+ LOG(ERROR) << "Unsupported battery path " << path;
+ return;
+ }
+
+ if (address.empty()) {
+ LOG(ERROR) << "No valid battery address at 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(address);
+ 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(address) == batteries_.end()) {
+ BatteryInfo battery(name, level, base::TimeTicks());
+ if (level <= kLowBatteryLevel) {
+ if (PostNotification(address, battery))
+ battery.last_notification_timestamp = testing_clock_ ?
+ testing_clock_->NowTicks() : base::TimeTicks::Now();
+ }
+ batteries_[address] = battery;
+ } else {
+ BatteryInfo* battery = &batteries_[address];
+ battery->name = name;
+ int old_level = battery->level;
+ battery->level = level;
+ if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) {
+ if (PostNotification(address, *battery))
+ battery->last_notification_timestamp = testing_clock_ ?
+ testing_clock_->NowTicks() : base::TimeTicks::Now();
+ }
+ }
+}
+
+void PeripheralBatteryObserver::DeviceChanged(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) {
+ if (!device->IsPaired())
+ RemoveBattery(device->GetAddress());
+}
+
+void PeripheralBatteryObserver::DeviceRemoved(device::BluetoothAdapter* adapter,
+ device::BluetoothDevice* device) {
+ RemoveBattery(device->GetAddress());
+}
+
+void PeripheralBatteryObserver::InitializeOnBluetoothReady(
+ scoped_refptr<device::BluetoothAdapter> adapter) {
+ bluetooth_adapter_ = adapter;
+ CHECK(bluetooth_adapter_);
+ bluetooth_adapter_->AddObserver(this);
+}
+
+void PeripheralBatteryObserver::RemoveBattery(const std::string& address) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ std::string address_lowercase = address;
+ StringToLowerASCII(&address_lowercase);
+ std::map<std::string, BatteryInfo>::iterator it =
+ batteries_.find(address_lowercase);
+ if (it != batteries_.end()) {
+ batteries_.erase(it);
+ CancelNotification(address_lowercase);
+ }
+}
+
+bool PeripheralBatteryObserver::PostNotification(const std::string& address,
+ 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.
+ base::TimeTicks now = testing_clock_ ? testing_clock_->NowTicks() :
+ base::TimeTicks::Now();
+ if (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(kNotificationOriginUrl),
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW),
+ UTF8ToUTF16(battery.name),
+ string_text,
+ WebKit::WebTextDirectionDefault,
+ string16(),
+ UTF8ToUTF16(address),
+ new PeripheralBatteryNotificationDelegate(address));
+
+ notification_manager->Add(notification,
+ ProfileManager::GetDefaultProfileOrOffTheRecord());
+
+ return true;
+}
+
+void PeripheralBatteryObserver::CancelNotification(const std::string& address) {
+ g_browser_process->notification_ui_manager()->CancelById(address);
+}
+
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698