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