Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / power / peripheral_battery_observer.cc
blob1ef701e20408377ca7623ee0478eb293265861f0
1 // Copyright (c) 2013 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.
5 #include "chrome/browser/chromeos/power/peripheral_battery_observer.h"
7 #include <vector>
9 #include "ash/shell.h"
10 #include "ash/strings/grit/ash_strings.h"
11 #include "base/bind.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/notifications/notification.h"
19 #include "chrome/browser/notifications/notification_ui_manager.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/grit/theme_resources.h"
22 #include "chromeos/dbus/dbus_thread_manager.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "device/bluetooth/bluetooth_adapter_factory.h"
25 #include "device/bluetooth/bluetooth_device.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/image/image.h"
30 namespace chromeos {
32 namespace {
34 // When a peripheral device's battery level is <= kLowBatteryLevel, consider
35 // it to be in low battery condition.
36 const int kLowBatteryLevel = 15;
38 // Don't show 2 low battery notification within |kNotificationIntervalSec|
39 // seconds.
40 const int kNotificationIntervalSec = 60;
42 const char kNotificationOriginUrl[] = "chrome://peripheral-battery";
44 // HID Bluetooth device's battery sysfs entry path looks like
45 // "/sys/class/power_supply/hid-AA:BB:CC:DD:EE:FF-battery".
46 // Here the bluetooth address is showed in reverse order and its true
47 // address "FF:EE:DD:CC:BB:AA".
48 const char kHIDBatteryPathPrefix[] = "/sys/class/power_supply/hid-";
49 const char kHIDBatteryPathSuffix[] = "-battery";
51 bool IsBluetoothHIDBattery(const std::string& path) {
52 return base::StartsWith(path, kHIDBatteryPathPrefix,
53 base::CompareCase::INSENSITIVE_ASCII) &&
54 base::EndsWith(path, kHIDBatteryPathSuffix,
55 base::CompareCase::INSENSITIVE_ASCII);
58 std::string ExtractBluetoothAddress(const std::string& path) {
59 int header_size = strlen(kHIDBatteryPathPrefix);
60 int end_size = strlen(kHIDBatteryPathSuffix);
61 int key_len = path.size() - header_size - end_size;
62 if (key_len <= 0)
63 return std::string();
64 std::string reverse_address = path.substr(header_size, key_len);
65 base::StringToLowerASCII(&reverse_address);
66 std::vector<std::string> result = base::SplitString(
67 reverse_address, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
68 std::reverse(result.begin(), result.end());
69 std::string address = base::JoinString(result, ":");
70 return address;
73 class PeripheralBatteryNotificationDelegate : public NotificationDelegate {
74 public:
75 explicit PeripheralBatteryNotificationDelegate(const std::string& id)
76 : id_(id) {}
78 // Overridden from NotificationDelegate:
79 std::string id() const override { return id_; }
81 private:
82 ~PeripheralBatteryNotificationDelegate() override {}
84 const std::string id_;
86 DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate);
89 } // namespace
91 PeripheralBatteryObserver::PeripheralBatteryObserver()
92 : testing_clock_(NULL),
93 notification_profile_(NULL),
94 weakptr_factory_(
95 new base::WeakPtrFactory<PeripheralBatteryObserver>(this)) {
96 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
97 device::BluetoothAdapterFactory::GetAdapter(
98 base::Bind(&PeripheralBatteryObserver::InitializeOnBluetoothReady,
99 weakptr_factory_->GetWeakPtr()));
102 PeripheralBatteryObserver::~PeripheralBatteryObserver() {
103 if (bluetooth_adapter_.get())
104 bluetooth_adapter_->RemoveObserver(this);
105 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
108 void PeripheralBatteryObserver::PeripheralBatteryStatusReceived(
109 const std::string& path,
110 const std::string& name,
111 int level) {
112 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
113 std::string address;
114 if (IsBluetoothHIDBattery(path)) {
115 // For HID bluetooth device, device address is used as key to index
116 // BatteryInfo.
117 address = ExtractBluetoothAddress(path);
118 } else {
119 LOG(ERROR) << "Unsupported battery path " << path;
120 return;
123 if (address.empty()) {
124 LOG(ERROR) << "No valid battery address at path " << path;
125 return;
128 if (level < -1 || level > 100) {
129 LOG(ERROR) << "Invalid battery level " << level
130 << " for device " << name << " at path " << path;
131 return;
133 // If unknown battery level received, cancel any existing notification.
134 if (level == -1) {
135 CancelNotification(address);
136 return;
139 // Post the notification in 2 cases:
140 // 1. It's the first time the battery level is received, and it is
141 // below kLowBatteryLevel.
142 // 2. The battery level is in record and it drops below kLowBatteryLevel.
143 if (batteries_.find(address) == batteries_.end()) {
144 BatteryInfo battery(name, level, base::TimeTicks());
145 if (level <= kLowBatteryLevel) {
146 if (PostNotification(address, battery))
147 battery.last_notification_timestamp = testing_clock_ ?
148 testing_clock_->NowTicks() : base::TimeTicks::Now();
150 batteries_[address] = battery;
151 } else {
152 BatteryInfo* battery = &batteries_[address];
153 battery->name = name;
154 int old_level = battery->level;
155 battery->level = level;
156 if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) {
157 if (PostNotification(address, *battery))
158 battery->last_notification_timestamp = testing_clock_ ?
159 testing_clock_->NowTicks() : base::TimeTicks::Now();
164 void PeripheralBatteryObserver::DeviceChanged(device::BluetoothAdapter* adapter,
165 device::BluetoothDevice* device) {
166 if (!device->IsPaired())
167 RemoveBattery(device->GetAddress());
170 void PeripheralBatteryObserver::DeviceRemoved(device::BluetoothAdapter* adapter,
171 device::BluetoothDevice* device) {
172 RemoveBattery(device->GetAddress());
175 void PeripheralBatteryObserver::InitializeOnBluetoothReady(
176 scoped_refptr<device::BluetoothAdapter> adapter) {
177 bluetooth_adapter_ = adapter;
178 CHECK(bluetooth_adapter_.get());
179 bluetooth_adapter_->AddObserver(this);
182 void PeripheralBatteryObserver::RemoveBattery(const std::string& address) {
183 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
184 std::string address_lowercase = address;
185 base::StringToLowerASCII(&address_lowercase);
186 std::map<std::string, BatteryInfo>::iterator it =
187 batteries_.find(address_lowercase);
188 if (it != batteries_.end()) {
189 batteries_.erase(it);
190 CancelNotification(address_lowercase);
194 bool PeripheralBatteryObserver::PostNotification(const std::string& address,
195 const BatteryInfo& battery) {
196 // Only post notification if kNotificationInterval seconds have passed since
197 // last notification showed, avoiding the case where the battery level
198 // oscillates around the threshold level.
199 base::TimeTicks now = testing_clock_ ? testing_clock_->NowTicks() :
200 base::TimeTicks::Now();
201 if (now - battery.last_notification_timestamp <
202 base::TimeDelta::FromSeconds(kNotificationIntervalSec))
203 return false;
205 NotificationUIManager* notification_manager =
206 g_browser_process->notification_ui_manager();
208 base::string16 string_text = l10n_util::GetStringFUTF16Int(
209 IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT,
210 battery.level);
212 Notification notification(
213 message_center::NOTIFICATION_TYPE_SIMPLE, GURL(kNotificationOriginUrl),
214 base::UTF8ToUTF16(battery.name), string_text,
215 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
216 IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW),
217 message_center::NotifierId(GURL(kNotificationOriginUrl)),
218 base::string16(), address, message_center::RichNotificationData(),
219 new PeripheralBatteryNotificationDelegate(address));
221 notification.set_priority(message_center::SYSTEM_PRIORITY);
223 notification_profile_ = ProfileManager::GetPrimaryUserProfile();
224 notification_manager->Add(notification, notification_profile_);
226 return true;
229 void PeripheralBatteryObserver::CancelNotification(const std::string& address) {
230 // If last_used_profile_ is NULL then no notification has been posted yet.
231 if (notification_profile_) {
232 g_browser_process->notification_ui_manager()->CancelById(
233 address, NotificationUIManager::GetProfileID(notification_profile_));
237 } // namespace chromeos