diff options
-rw-r--r-- | ash/ash_strings.grd | 3 | ||||
-rw-r--r-- | chrome/app/theme/theme_resources.grd | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/chrome_browser_main_chromeos.cc | 4 | ||||
-rw-r--r-- | chrome/browser/chromeos/chrome_browser_main_chromeos.h | 2 | ||||
-rw-r--r-- | chrome/browser/chromeos/power/peripheral_battery_observer.cc | 239 | ||||
-rw-r--r-- | chrome/browser/chromeos/power/peripheral_battery_observer.h | 99 | ||||
-rw-r--r-- | chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc | 158 | ||||
-rw-r--r-- | chrome/chrome_browser_chromeos.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
9 files changed, 511 insertions, 0 deletions
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 8b1cf38..14de4a9 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -457,6 +457,9 @@ Press Shift + Alt to switch. <message name="IDS_ASH_INTERNAL_DISPLAY_NAME" desc="The name of the internal display which is shown in the display settings."> Internal Display </message> + <message name="IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT" desc="The text of the notification when a peripheral device is in low battery condition."> + Battery low (<ph name="percentage">$1<ex>56</ex></ph>%) + </message> <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS" desc="The title of the notification when a screenshot was taken."> Screenshot taken </message> diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index c803e2a..ad0c91c 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd @@ -510,6 +510,9 @@ <structure type="chrome_scaled_image" name="IDR_NEWTAB_CHROME_WELCOME_PAGE_THUMBNAIL" file="common/ntp_welcome_thumb.png" /> </if> <structure type="chrome_scaled_image" name="IDR_NEWTAB_WEBSTORE_THUMBNAIL" file="ntp_webstore_thumb.png" /> + <if expr="pp_ifdef('chromeos')"> + <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW" file="cros/notification_peripheral_battery_low.png" /> + </if> <if expr="is_win"> <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_DIM" file="win/notification_tray_dim.png" /> <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_TRAY_LIT" file="win/notification_tray_lit.png" /> diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index d3acb3a..8c6c20f 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -56,6 +56,7 @@ #include "chrome/browser/chromeos/net/network_portal_detector.h" #include "chrome/browser/chromeos/power/brightness_observer.h" #include "chrome/browser/chromeos/power/idle_action_warning_observer.h" +#include "chrome/browser/chromeos/power/peripheral_battery_observer.h" #include "chrome/browser/chromeos/power/power_button_observer.h" #include "chrome/browser/chromeos/power/resume_observer.h" #include "chrome/browser/chromeos/power/screen_lock_observer.h" @@ -634,6 +635,8 @@ void ChromeBrowserMainPartsChromeos::PostProfileInit() { } chromeos::accessibility::Initialize(); + peripheral_battery_observer_.reset(new PeripheralBatteryObserver()); + storage_monitor_->Init(); // Initialize the network portal detector for Chrome OS. The network @@ -738,6 +741,7 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() { resume_observer_.reset(); brightness_observer_.reset(); retail_mode_power_save_blocker_.reset(); + peripheral_battery_observer_.reset(); // The XInput2 event listener needs to be shut down earlier than when // Singletons are finally destroyed in AtExitManager. diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h index 8da40ad..3f18070 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h @@ -25,6 +25,7 @@ class BrightnessObserver; class DisplayConfigurationObserver; class IdleActionWarningObserver; class MagnificationManager; +class PeripheralBatteryObserver; class PowerButtonObserver; class ResumeObserver; class ScreenLockObserver; @@ -80,6 +81,7 @@ class ChromeBrowserMainPartsChromeos : public ChromeBrowserMainPartsLinux { scoped_ptr<ResumeObserver> resume_observer_; scoped_ptr<ScreenLockObserver> screen_lock_observer_; scoped_ptr<ScreensaverController> screensaver_controller_; + scoped_ptr<PeripheralBatteryObserver> peripheral_battery_observer_; scoped_ptr<PowerButtonObserver> power_button_observer_; scoped_ptr<content::PowerSaveBlocker> retail_mode_power_save_blocker_; scoped_ptr<UserActivityNotifier> user_activity_notifier_; 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 0000000..f2487bc --- /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 diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer.h b/chrome/browser/chromeos/power/peripheral_battery_observer.h new file mode 100644 index 0000000..0e958be --- /dev/null +++ b/chrome/browser/chromeos/power/peripheral_battery_observer.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef CHROME_BROWSER_CHROMEOS_POWER_PERIPHERAL_BATTERY_OBSERVER_H_ +#define CHROME_BROWSER_CHROMEOS_POWER_PERIPHERAL_BATTERY_OBSERVER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" +#include "base/test/simple_test_tick_clock.h" +#include "chromeos/dbus/power_manager_client.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace chromeos { + +class BluetoothDevice; +class PeripheralBatteryObserverTest; + +// This observer listens for peripheral device battery status and shows +// notifications for low battery conditions. +class PeripheralBatteryObserver : public PowerManagerClient::Observer, + public device::BluetoothAdapter::Observer { + public: + // This class registers/unregisters itself as an observer in ctor/dtor. + PeripheralBatteryObserver(); + virtual ~PeripheralBatteryObserver(); + + void set_testing_clock(base::SimpleTestTickClock* clock) { + testing_clock_ = clock; + } + + // PowerManagerClient::Observer implementation. + virtual void PeripheralBatteryStatusReceived(const std::string& path, + const std::string& name, + int level) OVERRIDE; + + // device::BluetoothAdapter::Observer implementation. + virtual void DeviceChanged(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) OVERRIDE; + virtual void DeviceRemoved(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) OVERRIDE; + + private: + friend class PeripheralBatteryObserverTest; + FRIEND_TEST_ALL_PREFIXES(PeripheralBatteryObserverTest, Basic); + FRIEND_TEST_ALL_PREFIXES(PeripheralBatteryObserverTest, InvalidBatteryInfo); + FRIEND_TEST_ALL_PREFIXES(PeripheralBatteryObserverTest, DeviceRemove); + + struct BatteryInfo { + BatteryInfo() : level(-1) {} + BatteryInfo(const std::string& name, + int level, + base::TimeTicks notification_timestamp) + : name(name), + level(level), + last_notification_timestamp(notification_timestamp) { + } + + // Human readable name for the device. It is changeable. + std::string name; + // Battery level within range [0, 100], and -1 for unknown level. + int level; + base::TimeTicks last_notification_timestamp; + }; + + void InitializeOnBluetoothReady( + scoped_refptr<device::BluetoothAdapter> adapter); + + void RemoveBattery(const std::string& address); + + // Posts a low battery notification with unique id |address|. Returns true + // if the notification is posted, false if not. + bool PostNotification(const std::string& address, const BatteryInfo& battery); + + void CancelNotification(const std::string& address); + + // Record of existing battery infomation. For bluetooth HID device, the key + // is the address of the bluetooth device. + std::map<std::string, BatteryInfo> batteries_; + + // PeripheralBatteryObserver is an observer of |bluetooth_adapter_| for + // bluetooth device change/remove events. + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_; + + // Used only for helping test. Not owned and can be NULL. + base::SimpleTestTickClock* testing_clock_; + + scoped_ptr<base::WeakPtrFactory<PeripheralBatteryObserver> > weakptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryObserver); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_POWER_PERIPHERAL_BATTERY_OBSERVER_H_ diff --git a/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc b/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc new file mode 100644 index 0000000..533a5bb --- /dev/null +++ b/chrome/browser/chromeos/power/peripheral_battery_observer_browsertest.cc @@ -0,0 +1,158 @@ +// Copyright 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 "base/command_line.h" +#include "base/message_loop.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h" +#include "chrome/browser/notifications/notification_ui_manager.h" +#include "chromeos/dbus/mock_dbus_thread_manager.h" +#include "chromeos/dbus/mock_update_engine_client.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_browser_thread.h" +#include "content/public/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SaveArg; + +namespace { + +const char kTestBatteryPath[] = "/sys/class/power_supply/hid-AA:BB:CC-battery"; +const char kTestBatteryAddress[] = "cc:bb:aa"; +const char kTestDeviceName[] = "test device"; + +} // namespace + +namespace chromeos { + +class PeripheralBatteryObserverTest : public CrosInProcessBrowserTest { + public: + PeripheralBatteryObserverTest () {} + virtual ~PeripheralBatteryObserverTest () {} + + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + MockDBusThreadManager* mock_dbus_thread_manager = new MockDBusThreadManager; + DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + CrosInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + } + + virtual void SetUpOnMainThread() OVERRIDE { + observer_.reset(new PeripheralBatteryObserver()); + } + + virtual void CleanUpOnMainThread() OVERRIDE { + observer_.reset(); + } + + virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { + CrosInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); + DBusThreadManager::Shutdown(); + } + + protected: + scoped_ptr<PeripheralBatteryObserver> observer_; + + private: + DISALLOW_COPY_AND_ASSIGN(PeripheralBatteryObserverTest); +}; + +IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, Basic) { + base::SimpleTestTickClock clock; + observer_->set_testing_clock(&clock); + + NotificationUIManager* notification_manager = + g_browser_process->notification_ui_manager(); + + // Level 50 at time 100, no low-battery notification. + clock.Advance(base::TimeDelta::FromSeconds(100)); + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 50); + EXPECT_EQ(observer_->batteries_.count(kTestBatteryAddress), 1u); + + const PeripheralBatteryObserver::BatteryInfo& info = + observer_->batteries_[kTestBatteryAddress]; + + EXPECT_EQ(info.name, kTestDeviceName); + EXPECT_EQ(info.level, 50); + EXPECT_EQ(info.last_notification_timestamp, base::TimeTicks()); + EXPECT_FALSE(notification_manager->DoesIdExist(kTestBatteryAddress)); + + // Level 5 at time 110, low-battery notification. + clock.Advance(base::TimeDelta::FromSeconds(10)); + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 5); + EXPECT_EQ(info.level, 5); + EXPECT_EQ(info.last_notification_timestamp, clock.NowTicks()); + EXPECT_TRUE(notification_manager->DoesIdExist(kTestBatteryAddress)); + + // Level -1 at time 115, cancel previous notification + clock.Advance(base::TimeDelta::FromSeconds(5)); + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, -1); + EXPECT_EQ(info.level, 5); + EXPECT_EQ(info.last_notification_timestamp, + clock.NowTicks() - base::TimeDelta::FromSeconds(5)); + EXPECT_FALSE(notification_manager->DoesIdExist(kTestBatteryAddress)); + + // Level 50 at time 120, no low-battery notification. + clock.Advance(base::TimeDelta::FromSeconds(5)); + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 50); + EXPECT_EQ(info.level, 50); + EXPECT_EQ(info.last_notification_timestamp, + clock.NowTicks() - base::TimeDelta::FromSeconds(10)); + EXPECT_FALSE(notification_manager->DoesIdExist(kTestBatteryAddress)); + + // Level 5 at time 130, no low-battery notification (throttling). + clock.Advance(base::TimeDelta::FromSeconds(10)); + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 5); + EXPECT_EQ(info.level, 5); + EXPECT_EQ(info.last_notification_timestamp, + clock.NowTicks() - base::TimeDelta::FromSeconds(20)); + EXPECT_FALSE(notification_manager->DoesIdExist(kTestBatteryAddress)); +} + +IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, InvalidBatteryInfo) { + observer_->PeripheralBatteryStatusReceived("invalid-path", kTestDeviceName, + 10); + EXPECT_TRUE(observer_->batteries_.empty()); + + observer_->PeripheralBatteryStatusReceived( + "/sys/class/power_supply/hid-battery", kTestDeviceName, 10); + EXPECT_TRUE(observer_->batteries_.empty()); + + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, -2); + EXPECT_TRUE(observer_->batteries_.empty()); + + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 101); + EXPECT_TRUE(observer_->batteries_.empty()); + + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, -1); + EXPECT_TRUE(observer_->batteries_.empty()); +} + +IN_PROC_BROWSER_TEST_F(PeripheralBatteryObserverTest, DeviceRemove) { + NotificationUIManager* notification_manager = + g_browser_process->notification_ui_manager(); + + observer_->PeripheralBatteryStatusReceived(kTestBatteryPath, + kTestDeviceName, 5); + EXPECT_EQ(observer_->batteries_.count(kTestBatteryAddress), 1u); + EXPECT_TRUE(notification_manager->DoesIdExist(kTestBatteryAddress)); + + observer_->RemoveBattery(kTestBatteryAddress); + EXPECT_FALSE(notification_manager->DoesIdExist(kTestBatteryAddress)); +} + +} // namespace chromeos diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index 084cb0b..a239e25 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -605,6 +605,8 @@ 'browser/chromeos/power/idle_action_warning_dialog_view.h', 'browser/chromeos/power/idle_action_warning_observer.cc', 'browser/chromeos/power/idle_action_warning_observer.h', + 'browser/chromeos/power/peripheral_battery_observer.cc', + 'browser/chromeos/power/peripheral_battery_observer.h', 'browser/chromeos/power/power_button_observer.cc', 'browser/chromeos/power/power_button_observer.h', 'browser/chromeos/power/resume_observer.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 100c17b..7e13ab6 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1211,6 +1211,7 @@ 'browser/chromeos/policy/device_status_collector_browsertest.cc', 'browser/chromeos/policy/policy_cert_verifier_browsertest.cc', 'browser/chromeos/policy/power_policy_browsertest.cc', + 'browser/chromeos/power/peripheral_battery_observer_browsertest.cc', 'browser/chromeos/screensaver/screensaver_controller_browsertest.cc', 'browser/chromeos/system/tray_accessibility_browsertest.cc', 'browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc', |