Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / power / peripheral_battery_observer.cc
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.
4
5 #include "chrome/browser/chromeos/power/peripheral_battery_observer.h"
6
7 #include <vector>
8
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"
29
30 namespace chromeos {
31
32 namespace {
33
34 // When a peripheral device's battery level is <= kLowBatteryLevel, consider
35 // it to be in low battery condition.
36 const int kLowBatteryLevel = 15;
37
38 // Don't show 2 low battery notification within |kNotificationIntervalSec|
39 // seconds.
40 const int kNotificationIntervalSec = 60;
41
42 const char kNotificationOriginUrl[] = "chrome://peripheral-battery";
43
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";
50
51 bool IsBluetoothHIDBattery(const std::string& path) {
52   return StartsWithASCII(path, kHIDBatteryPathPrefix, false) &&
53       EndsWith(path, kHIDBatteryPathSuffix, false);
54 }
55
56 std::string ExtractBluetoothAddress(const std::string& path) {
57   int header_size = strlen(kHIDBatteryPathPrefix);
58   int end_size = strlen(kHIDBatteryPathSuffix);
59   int key_len = path.size() - header_size - end_size;
60   if (key_len <= 0)
61     return std::string();
62   std::string reverse_address = path.substr(header_size, key_len);
63   base::StringToLowerASCII(&reverse_address);
64   std::vector<std::string> result;
65   base::SplitString(reverse_address, ':', &result);
66   std::reverse(result.begin(), result.end());
67   std::string address = JoinString(result, ':');
68   return address;
69 }
70
71 class PeripheralBatteryNotificationDelegate : public NotificationDelegate {
72  public:
73   explicit PeripheralBatteryNotificationDelegate(const std::string& id)
74       : id_(id) {}
75
76   // Overridden from NotificationDelegate:
77   virtual std::string id() const override { return id_; }
78
79  private:
80   virtual ~PeripheralBatteryNotificationDelegate() {}
81
82   const std::string id_;
83
84   DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate);
85 };
86
87 }  // namespace
88
89 PeripheralBatteryObserver::PeripheralBatteryObserver()
90     : testing_clock_(NULL),
91       notification_profile_(NULL),
92       weakptr_factory_(
93           new base::WeakPtrFactory<PeripheralBatteryObserver>(this)) {
94   DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
95   device::BluetoothAdapterFactory::GetAdapter(
96       base::Bind(&PeripheralBatteryObserver::InitializeOnBluetoothReady,
97                  weakptr_factory_->GetWeakPtr()));
98 }
99
100 PeripheralBatteryObserver::~PeripheralBatteryObserver() {
101   if (bluetooth_adapter_.get())
102     bluetooth_adapter_->RemoveObserver(this);
103   DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
104 }
105
106 void PeripheralBatteryObserver::PeripheralBatteryStatusReceived(
107     const std::string& path,
108     const std::string& name,
109     int level) {
110   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
111   std::string address;
112   if (IsBluetoothHIDBattery(path)) {
113     // For HID bluetooth device, device address is used as key to index
114     // BatteryInfo.
115     address = ExtractBluetoothAddress(path);
116   } else {
117     LOG(ERROR) << "Unsupported battery path " << path;
118     return;
119   }
120
121   if (address.empty()) {
122     LOG(ERROR) << "No valid battery address at path " << path;
123     return;
124   }
125
126   if (level < -1 || level > 100) {
127     LOG(ERROR) << "Invalid battery level " << level
128                << " for device " << name << " at path " << path;
129     return;
130   }
131   // If unknown battery level received, cancel any existing notification.
132   if (level == -1) {
133     CancelNotification(address);
134     return;
135   }
136
137   // Post the notification in 2 cases:
138   // 1. It's the first time the battery level is received, and it is
139   //    below kLowBatteryLevel.
140   // 2. The battery level is in record and it drops below kLowBatteryLevel.
141   if (batteries_.find(address) == batteries_.end()) {
142     BatteryInfo battery(name, level, base::TimeTicks());
143     if (level <= kLowBatteryLevel) {
144       if (PostNotification(address, battery))
145         battery.last_notification_timestamp = testing_clock_ ?
146             testing_clock_->NowTicks() : base::TimeTicks::Now();
147     }
148     batteries_[address] = battery;
149   } else {
150     BatteryInfo* battery = &batteries_[address];
151     battery->name = name;
152     int old_level = battery->level;
153     battery->level = level;
154     if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) {
155       if (PostNotification(address, *battery))
156         battery->last_notification_timestamp = testing_clock_ ?
157             testing_clock_->NowTicks() : base::TimeTicks::Now();
158     }
159   }
160 }
161
162 void PeripheralBatteryObserver::DeviceChanged(device::BluetoothAdapter* adapter,
163                                               device::BluetoothDevice* device) {
164   if (!device->IsPaired())
165     RemoveBattery(device->GetAddress());
166 }
167
168 void PeripheralBatteryObserver::DeviceRemoved(device::BluetoothAdapter* adapter,
169                                               device::BluetoothDevice* device) {
170   RemoveBattery(device->GetAddress());
171 }
172
173 void PeripheralBatteryObserver::InitializeOnBluetoothReady(
174     scoped_refptr<device::BluetoothAdapter> adapter) {
175   bluetooth_adapter_ = adapter;
176   CHECK(bluetooth_adapter_.get());
177   bluetooth_adapter_->AddObserver(this);
178 }
179
180 void PeripheralBatteryObserver::RemoveBattery(const std::string& address) {
181   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
182   std::string address_lowercase = address;
183   base::StringToLowerASCII(&address_lowercase);
184   std::map<std::string, BatteryInfo>::iterator it =
185       batteries_.find(address_lowercase);
186   if (it != batteries_.end()) {
187     batteries_.erase(it);
188     CancelNotification(address_lowercase);
189   }
190 }
191
192 bool PeripheralBatteryObserver::PostNotification(const std::string& address,
193                                                  const BatteryInfo& battery) {
194   // Only post notification if kNotificationInterval seconds have passed since
195   // last notification showed, avoiding the case where the battery level
196   // oscillates around the threshold level.
197   base::TimeTicks now = testing_clock_ ? testing_clock_->NowTicks() :
198       base::TimeTicks::Now();
199   if (now - battery.last_notification_timestamp <
200       base::TimeDelta::FromSeconds(kNotificationIntervalSec))
201     return false;
202
203   NotificationUIManager* notification_manager =
204       g_browser_process->notification_ui_manager();
205
206   base::string16 string_text = l10n_util::GetStringFUTF16Int(
207       IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT,
208       battery.level);
209
210   Notification notification(
211       message_center::NOTIFICATION_TYPE_SIMPLE,
212       GURL(kNotificationOriginUrl),
213       base::UTF8ToUTF16(battery.name),
214       string_text,
215       ui::ResourceBundle::GetSharedInstance().GetImageNamed(
216           IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW),
217       blink::WebTextDirectionDefault,
218       message_center::NotifierId(GURL(kNotificationOriginUrl)),
219       base::string16(),
220       base::UTF8ToUTF16(address),
221       message_center::RichNotificationData(),
222       new PeripheralBatteryNotificationDelegate(address));
223
224   notification.set_priority(message_center::SYSTEM_PRIORITY);
225
226   notification_profile_ = ProfileManager::GetPrimaryUserProfile();
227   notification_manager->Add(notification, notification_profile_);
228
229   return true;
230 }
231
232 void PeripheralBatteryObserver::CancelNotification(const std::string& address) {
233   // If last_used_profile_ is NULL then no notification has been posted yet.
234   if (notification_profile_) {
235     g_browser_process->notification_ui_manager()->CancelById(
236         address, NotificationUIManager::GetProfileID(notification_profile_));
237   }
238 }
239
240 }  // namespace chromeos