1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/power/peripheral_battery_observer.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <vector>
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ash/shell.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/bind.h"
115e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string16.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/string_split.h"
135e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/browser_process.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/notifications/notification.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/notifications/notification_ui_manager.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "device/bluetooth/bluetooth_adapter_factory.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "device/bluetooth/bluetooth_device.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "grit/ash_strings.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "grit/theme_resources.h"
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/gfx/image/image.h"
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace chromeos {
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// When a peripheral device's battery level is <= kLowBatteryLevel, consider
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// it to be in low battery condition.
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)const int kLowBatteryLevel = 15;
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Don't show 2 low battery notification within |kNotificationIntervalSec|
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// seconds.
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kNotificationIntervalSec = 60;
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kNotificationOriginUrl[] = "chrome://peripheral-battery";
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// HID Bluetooth device's battery sysfs entry path looks like
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// "/sys/class/power_supply/hid-AA:BB:CC:DD:EE:FF-battery".
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Here the bluetooth address is showed in reverse order and its true
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// address "FF:EE:DD:CC:BB:AA".
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kHIDBatteryPathPrefix[] = "/sys/class/power_supply/hid-";
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kHIDBatteryPathSuffix[] = "-battery";
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool IsBluetoothHIDBattery(const std::string& path) {
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return StartsWithASCII(path, kHIDBatteryPathPrefix, false) &&
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      EndsWith(path, kHIDBatteryPathSuffix, false);
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)std::string ExtractBluetoothAddress(const std::string& path) {
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int header_size = strlen(kHIDBatteryPathPrefix);
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int end_size = strlen(kHIDBatteryPathSuffix);
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int key_len = path.size() - header_size - end_size;
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (key_len <= 0)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return std::string();
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string reverse_address = path.substr(header_size, key_len);
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  StringToLowerASCII(&reverse_address);
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<std::string> result;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::SplitString(reverse_address, ':', &result);
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::reverse(result.begin(), result.end());
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string address = JoinString(result, ':');
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return address;
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class PeripheralBatteryNotificationDelegate : public NotificationDelegate {
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public:
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  explicit PeripheralBatteryNotificationDelegate(const std::string& id)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : id_(id) {}
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Overridden from NotificationDelegate:
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void Display() OVERRIDE {}
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void Error() OVERRIDE {}
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void Close(bool by_user) OVERRIDE {}
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual void Click() OVERRIDE {}
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual std::string id() const OVERRIDE { return id_; }
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // A NULL return value prevents loading image from URL. It is OK since our
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // implementation loads image from system resource bundle.
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return NULL;
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private:
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual ~PeripheralBatteryNotificationDelegate() {}
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const std::string id_;
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryNotificationDelegate);
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)PeripheralBatteryObserver::PeripheralBatteryObserver()
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : testing_clock_(NULL),
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weakptr_factory_(
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          new base::WeakPtrFactory<PeripheralBatteryObserver>(this)) {
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  device::BluetoothAdapterFactory::GetAdapter(
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&PeripheralBatteryObserver::InitializeOnBluetoothReady,
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 weakptr_factory_->GetWeakPtr()));
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)PeripheralBatteryObserver::~PeripheralBatteryObserver() {
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (bluetooth_adapter_.get())
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    bluetooth_adapter_->RemoveObserver(this);
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::PeripheralBatteryStatusReceived(
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& path,
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& name,
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int level) {
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string address;
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (IsBluetoothHIDBattery(path)) {
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // For HID bluetooth device, device address is used as key to index
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // BatteryInfo.
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    address = ExtractBluetoothAddress(path);
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(ERROR) << "Unsupported battery path " << path;
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (address.empty()) {
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(ERROR) << "No valid battery address at path " << path;
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (level < -1 || level > 100) {
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(ERROR) << "Invalid battery level " << level
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               << " for device " << name << " at path " << path;
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If unknown battery level received, cancel any existing notification.
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (level == -1) {
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CancelNotification(address);
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Post the notification in 2 cases:
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // 1. It's the first time the battery level is received, and it is
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  //    below kLowBatteryLevel.
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // 2. The battery level is in record and it drops below kLowBatteryLevel.
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (batteries_.find(address) == batteries_.end()) {
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    BatteryInfo battery(name, level, base::TimeTicks());
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (level <= kLowBatteryLevel) {
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (PostNotification(address, battery))
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        battery.last_notification_timestamp = testing_clock_ ?
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            testing_clock_->NowTicks() : base::TimeTicks::Now();
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    batteries_[address] = battery;
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    BatteryInfo* battery = &batteries_[address];
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    battery->name = name;
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int old_level = battery->level;
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    battery->level = level;
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (old_level > kLowBatteryLevel && level <= kLowBatteryLevel) {
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (PostNotification(address, *battery))
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        battery->last_notification_timestamp = testing_clock_ ?
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            testing_clock_->NowTicks() : base::TimeTicks::Now();
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::DeviceChanged(device::BluetoothAdapter* adapter,
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                              device::BluetoothDevice* device) {
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!device->IsPaired())
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    RemoveBattery(device->GetAddress());
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::DeviceRemoved(device::BluetoothAdapter* adapter,
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                              device::BluetoothDevice* device) {
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RemoveBattery(device->GetAddress());
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::InitializeOnBluetoothReady(
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    scoped_refptr<device::BluetoothAdapter> adapter) {
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bluetooth_adapter_ = adapter;
1837d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  CHECK(bluetooth_adapter_.get());
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bluetooth_adapter_->AddObserver(this);
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::RemoveBattery(const std::string& address) {
188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string address_lowercase = address;
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  StringToLowerASCII(&address_lowercase);
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::map<std::string, BatteryInfo>::iterator it =
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      batteries_.find(address_lowercase);
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (it != batteries_.end()) {
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    batteries_.erase(it);
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CancelNotification(address_lowercase);
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool PeripheralBatteryObserver::PostNotification(const std::string& address,
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                 const BatteryInfo& battery) {
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Only post notification if kNotificationInterval seconds have passed since
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // last notification showed, avoiding the case where the battery level
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // oscillates around the threshold level.
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::TimeTicks now = testing_clock_ ? testing_clock_->NowTicks() :
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::TimeTicks::Now();
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (now - battery.last_notification_timestamp <
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::TimeDelta::FromSeconds(kNotificationIntervalSec))
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  NotificationUIManager* notification_manager =
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      g_browser_process->notification_ui_manager();
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::string16 string_text = l10n_util::GetStringFUTF16Int(
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT,
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      battery.level);
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Notification notification(
218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // TODO(mukai): add SYSTEM priority and use here.
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      GURL(kNotificationOriginUrl),
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      ui::ResourceBundle::GetSharedInstance().GetImageNamed(
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW),
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      UTF8ToUTF16(battery.name),
223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      string_text,
224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      WebKit::WebTextDirectionDefault,
225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      string16(),
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      UTF8ToUTF16(address),
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new PeripheralBatteryNotificationDelegate(address));
228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  notification_manager->Add(notification,
230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            ProfileManager::GetDefaultProfileOrOffTheRecord());
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PeripheralBatteryObserver::CancelNotification(const std::string& address) {
236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  g_browser_process->notification_ui_manager()->CancelById(address);
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace chromeos
240