123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- // Copyright 2020 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 "ash/system/power/peripheral_battery_listener.h"
- #include <string>
- #include <vector>
- #include "ash/power/hid_battery_util.h"
- #include "ash/public/cpp/notification_utils.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/memory/weak_ptr.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "base/strings/utf_string_conversions.h"
- #include "device/bluetooth/bluetooth_adapter_factory.h"
- #include "device/bluetooth/bluetooth_device.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "third_party/re2/src/re2/re2.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/events/devices/device_data_manager.h"
- #include "ui/events/devices/stylus_state.h"
- #include "ui/events/devices/touchscreen_device.h"
- #include "ui/gfx/image/image.h"
- #include "ui/message_center/message_center.h"
- #include "ui/message_center/public/cpp/notification.h"
- #include "ui/ozone/public/input_controller.h"
- #include "ui/ozone/public/ozone_platform.h"
- namespace ash {
- namespace {
- constexpr char kBluetoothDeviceIdPrefix[] = "battery_bluetooth-";
- // Currently we expect at most one peripheral charger to exist, and
- // it will always be the stylus charger.
- // TODO(b/215381232): Temporarily support both PCHG name and peripheral name
- // till upstream kernel driver is merged.
- constexpr LazyRE2 kPeripheralChargerRegex = {
- R"(/(?:peripheral|PCHG)(?:[0-9]+)$)"};
- constexpr char kStylusChargerID[] = "peripheral0";
- // TODO(b/187298772,b/187299765): if we have docked stylus chargers that have
- // significantly different parameters, we will need to provide a way to
- // dynamically configure these parameters, or else have the EC provide a PCHG0
- // device directly to model the charger, and modify this logic to disable the
- // synthetic charger when both a switch and a PCHG0 device are present.
- // Millisecond period to update charge level when stylus is in garage
- constexpr int kGarageChargeUpdatePeriod = 1000;
- // Estimated maximum time to charge garaged stylus to 100%, in ms, plus margin
- constexpr int kGaragedStylusChargeTime = 17 * 1000;
- constexpr char kStylusGarageKey[] = "garaged-stylus-charger";
- constexpr char16_t kStylusGarageName[] = u"Stylus Charger";
- // Serial numbers for styluses which may report inconsistent battery levels.
- const RE2 kBlockedStylusDevicesPattern(
- "(?i)^(019|015|020|201|211|213)[0-9A-F]{5}(11|4[F0])FE368C$");
- // Serial numbers for styluses which may report inconsistent battery levels,
- // but might not actually exist in wild.
- const RE2 kUnusualStylusDevicesPattern(
- "(?i)^[0-9A-F]{3}[0-9A-F]{5}[0-9A-F]{2}FE368C$");
- // Checks if the device is an external stylus.
- bool IsStylusDevice(const std::string& path,
- const std::string& model_name,
- bool* has_garage) {
- std::string identifier = ExtractHIDBatteryIdentifier(path);
- for (const ui::TouchscreenDevice& device :
- ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) {
- if (device.has_stylus &&
- (device.name == model_name ||
- device.name.find(model_name) != std::string::npos) &&
- device.sys_path.value().find(identifier) != std::string::npos) {
- *has_garage = device.has_stylus_garage_switch;
- return true;
- }
- }
- return false;
- }
- // Checks for devices which are ineligible for battery reports.
- bool IsEligibleForBatteryReport(
- PeripheralBatteryListener::BatteryInfo::PeripheralType type,
- const std::string& serial_number) {
- if (type != PeripheralBatteryListener::BatteryInfo::PeripheralType::
- kStylusViaScreen &&
- type != PeripheralBatteryListener::BatteryInfo::PeripheralType::
- kStylusViaCharger)
- return true;
- // BUG(b/194132391): some firmwares do not report serial numbers,
- // treat them as ineligible until this is resolved.
- if (type == PeripheralBatteryListener::BatteryInfo::PeripheralType::
- kStylusViaScreen &&
- serial_number.empty())
- return false;
- if (serial_number.empty())
- return true;
- // TODO(b/188811631): Add metrics
- if (RE2::FullMatch(serial_number, kBlockedStylusDevicesPattern))
- return false;
- // kUnusualStylusDevicesPattern and unrecognized devices are eligible
- return true;
- }
- // Checks if device is the internal charger for an external stylus.
- bool IsPeripheralCharger(const std::string& path) {
- return RE2::PartialMatch(path, *kPeripheralChargerRegex);
- }
- std::string GetMapKeyForBluetoothAddress(const std::string& bluetooth_address) {
- return kBluetoothDeviceIdPrefix + base::ToLowerASCII(bluetooth_address);
- }
- // Returns the corresponding map key for a HID device.
- std::string GetBatteryMapKey(const std::string& path) {
- // Check if the HID path corresponds to a Bluetooth device.
- const std::string bluetooth_address =
- ExtractBluetoothAddressFromHIDBatteryPath(path);
- if (IsPeripheralCharger(path))
- return kStylusChargerID;
- else if (!bluetooth_address.empty())
- return GetMapKeyForBluetoothAddress(bluetooth_address);
- else
- return path;
- }
- std::string GetBatteryMapKey(device::BluetoothDevice* device) {
- return GetMapKeyForBluetoothAddress(device->GetAddress());
- }
- PeripheralBatteryListener::BatteryInfo::ChargeStatus
- ConvertPowerManagerChargeStatus(
- power_manager::PeripheralBatteryStatus_ChargeStatus incoming) {
- switch (incoming) {
- case power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_UNKNOWN:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kUnknown;
- case power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kDischarging;
- case power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kCharging;
- case power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_NOT_CHARGING:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kNotCharging;
- case power_manager::PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_FULL:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kFull;
- case power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_ERROR:
- return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kError;
- }
- }
- } // namespace
- PeripheralBatteryListener::BatteryInfo::BatteryInfo() = default;
- PeripheralBatteryListener::BatteryInfo::BatteryInfo(
- const std::string& key,
- const std::u16string& name,
- absl::optional<uint8_t> level,
- bool battery_report_eligible,
- base::TimeTicks last_update_timestamp,
- PeripheralType type,
- ChargeStatus charge_status,
- const std::string& bluetooth_address)
- : key(key),
- name(name),
- level(level),
- battery_report_eligible(battery_report_eligible),
- last_update_timestamp(last_update_timestamp),
- type(type),
- charge_status(charge_status),
- bluetooth_address(bluetooth_address) {}
- PeripheralBatteryListener::BatteryInfo::~BatteryInfo() = default;
- PeripheralBatteryListener::BatteryInfo::BatteryInfo(const BatteryInfo& info) =
- default;
- PeripheralBatteryListener::PeripheralBatteryListener() {
- chromeos::PowerManagerClient::Get()->AddObserver(this);
- ui::DeviceDataManager::GetInstance()->AddObserver(this);
- device::BluetoothAdapterFactory::Get()->GetAdapter(
- base::BindOnce(&PeripheralBatteryListener::InitializeOnBluetoothReady,
- weak_factory_.GetWeakPtr()));
- // When we are constructed, and device lists are ready, we want to get
- // the backlog of any peripheral devices from the pmc; otherwise we only
- // receive updates. If they aren't complete now, we'll catch it in the
- // callback when they are.
- if (ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete())
- chromeos::PowerManagerClient::Get()->RequestAllPeripheralBatteryUpdate();
- }
- PeripheralBatteryListener::~PeripheralBatteryListener() {
- garage_charge_timer_.Stop();
- if (bluetooth_adapter_)
- bluetooth_adapter_->RemoveObserver(this);
- ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
- if (chromeos::PowerManagerClient::Get())
- chromeos::PowerManagerClient::Get()->RemoveObserver(this);
- }
- bool PeripheralBatteryListener::HasSyntheticStylusGarargePeripheral() {
- return synthetic_stylus_garage_peripheral_;
- }
- void PeripheralBatteryListener::UpdateSyntheticStylusGarargePeripheral() {
- if (synthetic_stylus_garage_peripheral_)
- return;
- // When we start up, retrieve the current garage state. When we get
- // it, if the stylus is in the garage, assume it has been there for a
- // while and has a full charge. If not present, we cannot provide any
- // information.
- ui::OzonePlatform::GetInstance()->GetInputController()->GetStylusSwitchState(
- base::BindOnce(&PeripheralBatteryListener::GetSwitchStateCallback,
- weak_factory_.GetWeakPtr()));
- synthetic_stylus_garage_peripheral_ = true;
- }
- void PeripheralBatteryListener::GetSwitchStateCallback(ui::StylusState state) {
- BatteryInfo battery{kStylusGarageKey,
- kStylusGarageName,
- (state == ui::StylusState::REMOVED)
- ? absl::optional<uint8_t>(absl::nullopt)
- : 100,
- /*battery_report_eligible=*/true,
- base::TimeTicks::Now(),
- BatteryInfo::PeripheralType::kStylusViaCharger,
- (state == ui::StylusState::REMOVED)
- ? BatteryInfo::ChargeStatus::kUnknown
- : BatteryInfo::ChargeStatus::kFull,
- ""};
- UpdateBattery(battery, true);
- }
- // Observing chromeos::PowerManagerClient
- void PeripheralBatteryListener::PeripheralBatteryStatusReceived(
- const std::string& path,
- const std::string& name,
- int level,
- power_manager::PeripheralBatteryStatus_ChargeStatus pmc_charge_status,
- const std::string& serial_number,
- bool active_update) {
- // Note that zero levels are seen during boot on hid devices; a
- // power_supply node may be created without a real charge level, and
- // we must let it through to allow the BatteryInfo to be created as
- // soon as we are aware of it.
- if (level < -1 || level > 100) {
- LOG(ERROR) << "Invalid battery level " << level << " for device " << name
- << " at path " << path;
- return;
- }
- if (!IsHIDBattery(path) && !IsPeripheralCharger(path)) {
- LOG(ERROR) << "Unsupported battery path " << path;
- return;
- }
- if (!ui::DeviceDataManager::HasInstance() ||
- !ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete()) {
- LOG(ERROR) << "Discarding peripheral battery notification before devices "
- "are enumerated";
- return;
- }
- BatteryInfo::PeripheralType type;
- bool has_garage = false;
- if (IsPeripheralCharger(path)) {
- type = BatteryInfo::PeripheralType::kStylusViaCharger;
- // TODO(b/187299765): Devices currently do not both real peripheral chargers
- // and a stylus dock switch. Once that changes, this logic needs to be
- // updated to ensure the synthetic peripheral is not created.
- CHECK(!HasSyntheticStylusGarargePeripheral());
- } else if (IsStylusDevice(path, name, &has_garage)) {
- type = BatteryInfo::PeripheralType::kStylusViaScreen;
- if (has_garage) {
- UpdateSyntheticStylusGarargePeripheral();
- }
- } else {
- type = BatteryInfo::PeripheralType::kOther;
- }
- std::string map_key = GetBatteryMapKey(path);
- absl::optional<uint8_t> opt_level;
- // Discard: -1 level charge events, they, if they exist, are invalid.
- // 0-level discharge events can come through when hid devices
- // are created by the screen, and are not informative.
- // 0-level charging events are possible for peripheral wireless charging,
- // and are valid.
- if (level == -1 ||
- (level == 0 &&
- (type == BatteryInfo::PeripheralType::kStylusViaScreen ||
- type == BatteryInfo::PeripheralType::kStylusViaCharger) &&
- pmc_charge_status !=
- power_manager::
- PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING)) {
- opt_level = absl::nullopt;
- } else {
- opt_level = level;
- }
- // TODO(kenalba): if ineligible should we keep opt_level as previously set,
- // or clamp it to a fixed value?
- bool battery_report_eligible =
- IsEligibleForBatteryReport(type, serial_number);
- PeripheralBatteryListener::BatteryInfo battery{
- map_key,
- base::ASCIIToUTF16(name),
- opt_level,
- battery_report_eligible,
- base::TimeTicks::Now(),
- type,
- ConvertPowerManagerChargeStatus(pmc_charge_status),
- ExtractBluetoothAddressFromHIDBatteryPath(path)};
- UpdateBattery(battery, active_update);
- }
- // Observing device::BluetoothAdapter
- void PeripheralBatteryListener::DeviceBatteryChanged(
- device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- device::BluetoothDevice::BatteryType type) {
- // This class pre-dates the change to add multiple battery support. To reduce
- // the risk of regressions, we're ignoring battery updates for any new battery
- // types, and can revisit in the future if we decide there's a need for this
- // class to add support.
- if (type != device::BluetoothDevice::BatteryType::kDefault)
- return;
- absl::optional<device::BluetoothDevice::BatteryInfo> info =
- device->GetBatteryInfo(type);
- if (info && info->percentage)
- DCHECK_LE(info->percentage.value(), 100);
- BatteryInfo battery{GetBatteryMapKey(device),
- device->GetNameForDisplay(),
- info.has_value() ? info->percentage : absl::nullopt,
- /*battery_report_eligible=*/true,
- base::TimeTicks::Now(),
- BatteryInfo::PeripheralType::kOther,
- BatteryInfo::ChargeStatus::kUnknown,
- device->GetAddress()};
- // Bluetooth does not communicate charge state, do not fill in. Updates
- // will generally pull from the remote device, so consider them active.
- UpdateBattery(battery, /*active_update=*/true);
- }
- // Observing device::BluetoothAdapter
- void PeripheralBatteryListener::DeviceConnectedStateChanged(
- device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device,
- bool is_now_connected) {
- if (!is_now_connected) {
- RemoveBluetoothBattery(device->GetAddress());
- return;
- }
- for (auto type : device->GetAvailableBatteryTypes()) {
- absl::optional<device::BluetoothDevice::BatteryInfo> info =
- device->GetBatteryInfo(type);
- DCHECK(info);
- BatteryInfo::ChargeStatus charge_status;
- switch (info->charge_state) {
- case device::BluetoothDevice::BatteryInfo::ChargeState::kUnknown:
- charge_status = BatteryInfo::ChargeStatus::kUnknown;
- break;
- case device::BluetoothDevice::BatteryInfo::ChargeState::kCharging:
- charge_status = BatteryInfo::ChargeStatus::kCharging;
- break;
- case device::BluetoothDevice::BatteryInfo::ChargeState::kDischarging:
- charge_status = BatteryInfo::ChargeStatus::kDischarging;
- break;
- }
- BatteryInfo battery{GetBatteryMapKey(device),
- device->GetNameForDisplay(),
- info->percentage,
- /*battery_report_eligible=*/true,
- base::TimeTicks::Now(),
- BatteryInfo::PeripheralType::kOther,
- charge_status,
- device->GetAddress()};
- UpdateBattery(battery, /*active_update=*/true);
- }
- }
- // Observing device::BluetoothAdapter
- void PeripheralBatteryListener::DeviceRemoved(device::BluetoothAdapter* adapter,
- device::BluetoothDevice* device) {
- RemoveBluetoothBattery(device->GetAddress());
- }
- void PeripheralBatteryListener::InitializeOnBluetoothReady(
- scoped_refptr<device::BluetoothAdapter> adapter) {
- bluetooth_adapter_ = adapter;
- CHECK(bluetooth_adapter_);
- bluetooth_adapter_->AddObserver(this);
- }
- void PeripheralBatteryListener::RemoveBluetoothBattery(
- const std::string& bluetooth_address) {
- auto it = batteries_.find(kBluetoothDeviceIdPrefix +
- base::ToLowerASCII(bluetooth_address));
- if (it != batteries_.end()) {
- NotifyRemovingBattery(it->second);
- batteries_.erase(it);
- }
- }
- // Observing ui::DeviceDataManager:
- void PeripheralBatteryListener::OnDeviceListsComplete() {
- chromeos::PowerManagerClient::Get()->RequestAllPeripheralBatteryUpdate();
- }
- // Present a charge level and charging/full state based on the prior value. We
- // don't try to make an accurate estimate of charge level, as it could be
- // completely wrong. We instead assume that charging will always take the
- // maxmium amount of time, hold the charge level unchanged, at a max of 99% (not
- // fully charged), until the maximum charge time expires. Then it is reported at
- // 100% and full. This ensures it will not report full until it is _definitely_
- // full, and we don't provide a worse estimate than we were already showing.
- void PeripheralBatteryListener::GarageTimerAction(
- base::TimeTicks charge_start_time,
- absl::optional<uint8_t> start_level) {
- if (!synthetic_stylus_garage_peripheral_)
- return;
- auto it = batteries_.find(kStylusGarageKey);
- if (it == batteries_.end()) {
- return;
- }
- BatteryInfo info = it->second;
- info.last_update_timestamp = base::TimeTicks::Now();
- base::TimeDelta charge_period = base::TimeTicks::Now() - charge_start_time;
- int new_level = start_level.has_value() ? *start_level : 1;
- if (new_level < 1)
- new_level = 1;
- if (new_level >= 99)
- new_level = 99;
- // Consider it fully charged only after the max time has passed.
- if (charge_period.InMilliseconds() >= kGaragedStylusChargeTime) {
- info.level = 100;
- info.charge_status = BatteryInfo::ChargeStatus::kFull;
- garage_charge_timer_.Stop();
- } else {
- info.level = new_level;
- info.charge_status = BatteryInfo::ChargeStatus::kCharging;
- }
- UpdateBattery(info, true);
- }
- absl::optional<uint8_t> PeripheralBatteryListener::DerateLastChargeLevel() {
- BatteryInfo latest_battery;
- // Find the battery info with most recent data about the stylus
- for (auto it : batteries_) {
- if (it.second.type != BatteryInfo::PeripheralType::kStylusViaScreen &&
- it.second.type != BatteryInfo::PeripheralType::kStylusViaCharger) {
- continue;
- }
- if (!it.second.last_active_update_timestamp.has_value())
- continue;
- if (latest_battery.last_active_update_timestamp <
- it.second.last_active_update_timestamp) {
- latest_battery = it.second;
- }
- }
- // No information available.
- if (!latest_battery.level.has_value())
- return absl::nullopt;
- int level = *latest_battery.level;
- // We could do an estimate on charge level assuming a known discharge rate,
- // however we cannot prove it is the same stylus, and the operation would be
- // clearly incorrect if someone is swapping between two styluses to keep them
- // charged. Instead we simply report the last known level, or 99 at max, just
- // below full. This is not a correct estimate, but it is a useful value for
- // the UX. (99 max means we will never immediately say the stylus is full, and
- // using the last reading as minimum means we will never show 'low battery'
- // unless it was already the case).
- if (!level)
- level = 1;
- if (level >= 99)
- level = 99;
- return level;
- }
- void PeripheralBatteryListener::OnStylusStateChanged(
- ui::StylusState stylus_state) {
- if (!synthetic_stylus_garage_peripheral_)
- return;
- if (stylus_state == current_stylus_state_)
- return;
- auto it = batteries_.find(kStylusGarageKey);
- if (it == batteries_.end())
- return;
- BatteryInfo info = it->second;
- if (stylus_state == ui::StylusState::INSERTED) {
- // Set charger level from last prior reading, minus the estimated discharge
- // amount since the time of that last reading.
- info.level = DerateLastChargeLevel();
- info.charge_status = info.level >= 100
- ? BatteryInfo::ChargeStatus::kFull
- : BatteryInfo::ChargeStatus::kCharging;
- UpdateBattery(info, true);
- if (info.charge_status == BatteryInfo::ChargeStatus::kCharging) {
- base::TimeTicks charge_start_time = base::TimeTicks::Now();
- garage_charge_timer_.Start(
- FROM_HERE, base::Milliseconds(kGarageChargeUpdatePeriod),
- base::BindRepeating(&PeripheralBatteryListener::GarageTimerAction,
- base::Unretained(this), charge_start_time,
- info.level));
- } else {
- garage_charge_timer_.Stop();
- }
- } else if (stylus_state == ui::StylusState::REMOVED) {
- garage_charge_timer_.Stop();
- // We leave the charge level unchanged, it may not be accurate, but
- // it will be corrected once the stylus is used on the screen; any
- // alternative (revising the estimate, or reverting to the value at
- // the beginning of charge, if it wasn't fully charged) would lead to the
- // level jumping when the stylus is removed from the garage.
- info.charge_status = BatteryInfo::ChargeStatus::kUnknown;
- UpdateBattery(info, true);
- }
- current_stylus_state_ = stylus_state;
- }
- void PeripheralBatteryListener::UpdateBattery(const BatteryInfo& battery_info,
- bool active_update) {
- const std::string& map_key = battery_info.key;
- auto it = batteries_.find(map_key);
- if (it == batteries_.end()) {
- batteries_[map_key] = battery_info;
- NotifyAddingBattery(batteries_[map_key]);
- } else {
- BatteryInfo& existing_battery_info = it->second;
- // Only some fields should ever change.
- DCHECK(existing_battery_info.bluetooth_address == battery_info.bluetooth_address);
- DCHECK(existing_battery_info.type == battery_info.type);
- existing_battery_info.name = battery_info.name;
- // Ignore a null level for stylus charger updates: we want to memorize
- // the last known actual value. (The touchscreen controller firmware
- // already memorizes this, for that path).
- if (battery_info.type != BatteryInfo::PeripheralType::kStylusViaCharger ||
- battery_info.level)
- existing_battery_info.level = battery_info.level;
- existing_battery_info.last_update_timestamp =
- battery_info.last_update_timestamp;
- existing_battery_info.charge_status = battery_info.charge_status;
- existing_battery_info.battery_report_eligible =
- battery_info.battery_report_eligible;
- }
- BatteryInfo& info = batteries_[map_key];
- if (active_update) {
- info.last_active_update_timestamp = info.last_update_timestamp;
- }
- NotifyUpdatedBatteryLevel(info);
- }
- void PeripheralBatteryListener::NotifyAddingBattery(
- const BatteryInfo& battery) {
- for (auto& obs : observers_)
- obs.OnAddingBattery(battery);
- }
- void PeripheralBatteryListener::NotifyRemovingBattery(
- const BatteryInfo& battery) {
- for (auto& obs : observers_)
- obs.OnRemovingBattery(battery);
- }
- void PeripheralBatteryListener::NotifyUpdatedBatteryLevel(
- const BatteryInfo& battery) {
- for (auto& obs : observers_)
- obs.OnUpdatedBatteryLevel(battery);
- }
- void PeripheralBatteryListener::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
- // As possible latecomer, introduce observer to batteries that already exist.
- for (auto it : batteries_) {
- observer->OnAddingBattery(it.second);
- observer->OnUpdatedBatteryLevel(it.second);
- }
- }
- void PeripheralBatteryListener::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
- }
- bool PeripheralBatteryListener::HasObserver(const Observer* observer) const {
- return observers_.HasObserver(observer);
- }
- } // namespace ash
|