peripheral_battery_listener.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/system/power/peripheral_battery_listener.h"
  5. #include <string>
  6. #include <vector>
  7. #include "ash/power/hid_battery_util.h"
  8. #include "ash/public/cpp/notification_utils.h"
  9. #include "ash/resources/vector_icons/vector_icons.h"
  10. #include "ash/shell.h"
  11. #include "ash/strings/grit/ash_strings.h"
  12. #include "base/bind.h"
  13. #include "base/logging.h"
  14. #include "base/memory/weak_ptr.h"
  15. #include "base/strings/string_piece.h"
  16. #include "base/strings/string_util.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "device/bluetooth/bluetooth_adapter_factory.h"
  19. #include "device/bluetooth/bluetooth_device.h"
  20. #include "third_party/abseil-cpp/absl/types/optional.h"
  21. #include "third_party/re2/src/re2/re2.h"
  22. #include "ui/base/l10n/l10n_util.h"
  23. #include "ui/base/resource/resource_bundle.h"
  24. #include "ui/events/devices/device_data_manager.h"
  25. #include "ui/events/devices/stylus_state.h"
  26. #include "ui/events/devices/touchscreen_device.h"
  27. #include "ui/gfx/image/image.h"
  28. #include "ui/message_center/message_center.h"
  29. #include "ui/message_center/public/cpp/notification.h"
  30. #include "ui/ozone/public/input_controller.h"
  31. #include "ui/ozone/public/ozone_platform.h"
  32. namespace ash {
  33. namespace {
  34. constexpr char kBluetoothDeviceIdPrefix[] = "battery_bluetooth-";
  35. // Currently we expect at most one peripheral charger to exist, and
  36. // it will always be the stylus charger.
  37. // TODO(b/215381232): Temporarily support both PCHG name and peripheral name
  38. // till upstream kernel driver is merged.
  39. constexpr LazyRE2 kPeripheralChargerRegex = {
  40. R"(/(?:peripheral|PCHG)(?:[0-9]+)$)"};
  41. constexpr char kStylusChargerID[] = "peripheral0";
  42. // TODO(b/187298772,b/187299765): if we have docked stylus chargers that have
  43. // significantly different parameters, we will need to provide a way to
  44. // dynamically configure these parameters, or else have the EC provide a PCHG0
  45. // device directly to model the charger, and modify this logic to disable the
  46. // synthetic charger when both a switch and a PCHG0 device are present.
  47. // Millisecond period to update charge level when stylus is in garage
  48. constexpr int kGarageChargeUpdatePeriod = 1000;
  49. // Estimated maximum time to charge garaged stylus to 100%, in ms, plus margin
  50. constexpr int kGaragedStylusChargeTime = 17 * 1000;
  51. constexpr char kStylusGarageKey[] = "garaged-stylus-charger";
  52. constexpr char16_t kStylusGarageName[] = u"Stylus Charger";
  53. // Serial numbers for styluses which may report inconsistent battery levels.
  54. const RE2 kBlockedStylusDevicesPattern(
  55. "(?i)^(019|015|020|201|211|213)[0-9A-F]{5}(11|4[F0])FE368C$");
  56. // Serial numbers for styluses which may report inconsistent battery levels,
  57. // but might not actually exist in wild.
  58. const RE2 kUnusualStylusDevicesPattern(
  59. "(?i)^[0-9A-F]{3}[0-9A-F]{5}[0-9A-F]{2}FE368C$");
  60. // Checks if the device is an external stylus.
  61. bool IsStylusDevice(const std::string& path,
  62. const std::string& model_name,
  63. bool* has_garage) {
  64. std::string identifier = ExtractHIDBatteryIdentifier(path);
  65. for (const ui::TouchscreenDevice& device :
  66. ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) {
  67. if (device.has_stylus &&
  68. (device.name == model_name ||
  69. device.name.find(model_name) != std::string::npos) &&
  70. device.sys_path.value().find(identifier) != std::string::npos) {
  71. *has_garage = device.has_stylus_garage_switch;
  72. return true;
  73. }
  74. }
  75. return false;
  76. }
  77. // Checks for devices which are ineligible for battery reports.
  78. bool IsEligibleForBatteryReport(
  79. PeripheralBatteryListener::BatteryInfo::PeripheralType type,
  80. const std::string& serial_number) {
  81. if (type != PeripheralBatteryListener::BatteryInfo::PeripheralType::
  82. kStylusViaScreen &&
  83. type != PeripheralBatteryListener::BatteryInfo::PeripheralType::
  84. kStylusViaCharger)
  85. return true;
  86. // BUG(b/194132391): some firmwares do not report serial numbers,
  87. // treat them as ineligible until this is resolved.
  88. if (type == PeripheralBatteryListener::BatteryInfo::PeripheralType::
  89. kStylusViaScreen &&
  90. serial_number.empty())
  91. return false;
  92. if (serial_number.empty())
  93. return true;
  94. // TODO(b/188811631): Add metrics
  95. if (RE2::FullMatch(serial_number, kBlockedStylusDevicesPattern))
  96. return false;
  97. // kUnusualStylusDevicesPattern and unrecognized devices are eligible
  98. return true;
  99. }
  100. // Checks if device is the internal charger for an external stylus.
  101. bool IsPeripheralCharger(const std::string& path) {
  102. return RE2::PartialMatch(path, *kPeripheralChargerRegex);
  103. }
  104. std::string GetMapKeyForBluetoothAddress(const std::string& bluetooth_address) {
  105. return kBluetoothDeviceIdPrefix + base::ToLowerASCII(bluetooth_address);
  106. }
  107. // Returns the corresponding map key for a HID device.
  108. std::string GetBatteryMapKey(const std::string& path) {
  109. // Check if the HID path corresponds to a Bluetooth device.
  110. const std::string bluetooth_address =
  111. ExtractBluetoothAddressFromHIDBatteryPath(path);
  112. if (IsPeripheralCharger(path))
  113. return kStylusChargerID;
  114. else if (!bluetooth_address.empty())
  115. return GetMapKeyForBluetoothAddress(bluetooth_address);
  116. else
  117. return path;
  118. }
  119. std::string GetBatteryMapKey(device::BluetoothDevice* device) {
  120. return GetMapKeyForBluetoothAddress(device->GetAddress());
  121. }
  122. PeripheralBatteryListener::BatteryInfo::ChargeStatus
  123. ConvertPowerManagerChargeStatus(
  124. power_manager::PeripheralBatteryStatus_ChargeStatus incoming) {
  125. switch (incoming) {
  126. case power_manager::
  127. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_UNKNOWN:
  128. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kUnknown;
  129. case power_manager::
  130. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_DISCHARGING:
  131. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kDischarging;
  132. case power_manager::
  133. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING:
  134. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kCharging;
  135. case power_manager::
  136. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_NOT_CHARGING:
  137. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kNotCharging;
  138. case power_manager::PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_FULL:
  139. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kFull;
  140. case power_manager::
  141. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_ERROR:
  142. return PeripheralBatteryListener::BatteryInfo::ChargeStatus::kError;
  143. }
  144. }
  145. } // namespace
  146. PeripheralBatteryListener::BatteryInfo::BatteryInfo() = default;
  147. PeripheralBatteryListener::BatteryInfo::BatteryInfo(
  148. const std::string& key,
  149. const std::u16string& name,
  150. absl::optional<uint8_t> level,
  151. bool battery_report_eligible,
  152. base::TimeTicks last_update_timestamp,
  153. PeripheralType type,
  154. ChargeStatus charge_status,
  155. const std::string& bluetooth_address)
  156. : key(key),
  157. name(name),
  158. level(level),
  159. battery_report_eligible(battery_report_eligible),
  160. last_update_timestamp(last_update_timestamp),
  161. type(type),
  162. charge_status(charge_status),
  163. bluetooth_address(bluetooth_address) {}
  164. PeripheralBatteryListener::BatteryInfo::~BatteryInfo() = default;
  165. PeripheralBatteryListener::BatteryInfo::BatteryInfo(const BatteryInfo& info) =
  166. default;
  167. PeripheralBatteryListener::PeripheralBatteryListener() {
  168. chromeos::PowerManagerClient::Get()->AddObserver(this);
  169. ui::DeviceDataManager::GetInstance()->AddObserver(this);
  170. device::BluetoothAdapterFactory::Get()->GetAdapter(
  171. base::BindOnce(&PeripheralBatteryListener::InitializeOnBluetoothReady,
  172. weak_factory_.GetWeakPtr()));
  173. // When we are constructed, and device lists are ready, we want to get
  174. // the backlog of any peripheral devices from the pmc; otherwise we only
  175. // receive updates. If they aren't complete now, we'll catch it in the
  176. // callback when they are.
  177. if (ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete())
  178. chromeos::PowerManagerClient::Get()->RequestAllPeripheralBatteryUpdate();
  179. }
  180. PeripheralBatteryListener::~PeripheralBatteryListener() {
  181. garage_charge_timer_.Stop();
  182. if (bluetooth_adapter_)
  183. bluetooth_adapter_->RemoveObserver(this);
  184. ui::DeviceDataManager::GetInstance()->RemoveObserver(this);
  185. if (chromeos::PowerManagerClient::Get())
  186. chromeos::PowerManagerClient::Get()->RemoveObserver(this);
  187. }
  188. bool PeripheralBatteryListener::HasSyntheticStylusGarargePeripheral() {
  189. return synthetic_stylus_garage_peripheral_;
  190. }
  191. void PeripheralBatteryListener::UpdateSyntheticStylusGarargePeripheral() {
  192. if (synthetic_stylus_garage_peripheral_)
  193. return;
  194. // When we start up, retrieve the current garage state. When we get
  195. // it, if the stylus is in the garage, assume it has been there for a
  196. // while and has a full charge. If not present, we cannot provide any
  197. // information.
  198. ui::OzonePlatform::GetInstance()->GetInputController()->GetStylusSwitchState(
  199. base::BindOnce(&PeripheralBatteryListener::GetSwitchStateCallback,
  200. weak_factory_.GetWeakPtr()));
  201. synthetic_stylus_garage_peripheral_ = true;
  202. }
  203. void PeripheralBatteryListener::GetSwitchStateCallback(ui::StylusState state) {
  204. BatteryInfo battery{kStylusGarageKey,
  205. kStylusGarageName,
  206. (state == ui::StylusState::REMOVED)
  207. ? absl::optional<uint8_t>(absl::nullopt)
  208. : 100,
  209. /*battery_report_eligible=*/true,
  210. base::TimeTicks::Now(),
  211. BatteryInfo::PeripheralType::kStylusViaCharger,
  212. (state == ui::StylusState::REMOVED)
  213. ? BatteryInfo::ChargeStatus::kUnknown
  214. : BatteryInfo::ChargeStatus::kFull,
  215. ""};
  216. UpdateBattery(battery, true);
  217. }
  218. // Observing chromeos::PowerManagerClient
  219. void PeripheralBatteryListener::PeripheralBatteryStatusReceived(
  220. const std::string& path,
  221. const std::string& name,
  222. int level,
  223. power_manager::PeripheralBatteryStatus_ChargeStatus pmc_charge_status,
  224. const std::string& serial_number,
  225. bool active_update) {
  226. // Note that zero levels are seen during boot on hid devices; a
  227. // power_supply node may be created without a real charge level, and
  228. // we must let it through to allow the BatteryInfo to be created as
  229. // soon as we are aware of it.
  230. if (level < -1 || level > 100) {
  231. LOG(ERROR) << "Invalid battery level " << level << " for device " << name
  232. << " at path " << path;
  233. return;
  234. }
  235. if (!IsHIDBattery(path) && !IsPeripheralCharger(path)) {
  236. LOG(ERROR) << "Unsupported battery path " << path;
  237. return;
  238. }
  239. if (!ui::DeviceDataManager::HasInstance() ||
  240. !ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete()) {
  241. LOG(ERROR) << "Discarding peripheral battery notification before devices "
  242. "are enumerated";
  243. return;
  244. }
  245. BatteryInfo::PeripheralType type;
  246. bool has_garage = false;
  247. if (IsPeripheralCharger(path)) {
  248. type = BatteryInfo::PeripheralType::kStylusViaCharger;
  249. // TODO(b/187299765): Devices currently do not both real peripheral chargers
  250. // and a stylus dock switch. Once that changes, this logic needs to be
  251. // updated to ensure the synthetic peripheral is not created.
  252. CHECK(!HasSyntheticStylusGarargePeripheral());
  253. } else if (IsStylusDevice(path, name, &has_garage)) {
  254. type = BatteryInfo::PeripheralType::kStylusViaScreen;
  255. if (has_garage) {
  256. UpdateSyntheticStylusGarargePeripheral();
  257. }
  258. } else {
  259. type = BatteryInfo::PeripheralType::kOther;
  260. }
  261. std::string map_key = GetBatteryMapKey(path);
  262. absl::optional<uint8_t> opt_level;
  263. // Discard: -1 level charge events, they, if they exist, are invalid.
  264. // 0-level discharge events can come through when hid devices
  265. // are created by the screen, and are not informative.
  266. // 0-level charging events are possible for peripheral wireless charging,
  267. // and are valid.
  268. if (level == -1 ||
  269. (level == 0 &&
  270. (type == BatteryInfo::PeripheralType::kStylusViaScreen ||
  271. type == BatteryInfo::PeripheralType::kStylusViaCharger) &&
  272. pmc_charge_status !=
  273. power_manager::
  274. PeripheralBatteryStatus_ChargeStatus_CHARGE_STATUS_CHARGING)) {
  275. opt_level = absl::nullopt;
  276. } else {
  277. opt_level = level;
  278. }
  279. // TODO(kenalba): if ineligible should we keep opt_level as previously set,
  280. // or clamp it to a fixed value?
  281. bool battery_report_eligible =
  282. IsEligibleForBatteryReport(type, serial_number);
  283. PeripheralBatteryListener::BatteryInfo battery{
  284. map_key,
  285. base::ASCIIToUTF16(name),
  286. opt_level,
  287. battery_report_eligible,
  288. base::TimeTicks::Now(),
  289. type,
  290. ConvertPowerManagerChargeStatus(pmc_charge_status),
  291. ExtractBluetoothAddressFromHIDBatteryPath(path)};
  292. UpdateBattery(battery, active_update);
  293. }
  294. // Observing device::BluetoothAdapter
  295. void PeripheralBatteryListener::DeviceBatteryChanged(
  296. device::BluetoothAdapter* adapter,
  297. device::BluetoothDevice* device,
  298. device::BluetoothDevice::BatteryType type) {
  299. // This class pre-dates the change to add multiple battery support. To reduce
  300. // the risk of regressions, we're ignoring battery updates for any new battery
  301. // types, and can revisit in the future if we decide there's a need for this
  302. // class to add support.
  303. if (type != device::BluetoothDevice::BatteryType::kDefault)
  304. return;
  305. absl::optional<device::BluetoothDevice::BatteryInfo> info =
  306. device->GetBatteryInfo(type);
  307. if (info && info->percentage)
  308. DCHECK_LE(info->percentage.value(), 100);
  309. BatteryInfo battery{GetBatteryMapKey(device),
  310. device->GetNameForDisplay(),
  311. info.has_value() ? info->percentage : absl::nullopt,
  312. /*battery_report_eligible=*/true,
  313. base::TimeTicks::Now(),
  314. BatteryInfo::PeripheralType::kOther,
  315. BatteryInfo::ChargeStatus::kUnknown,
  316. device->GetAddress()};
  317. // Bluetooth does not communicate charge state, do not fill in. Updates
  318. // will generally pull from the remote device, so consider them active.
  319. UpdateBattery(battery, /*active_update=*/true);
  320. }
  321. // Observing device::BluetoothAdapter
  322. void PeripheralBatteryListener::DeviceConnectedStateChanged(
  323. device::BluetoothAdapter* adapter,
  324. device::BluetoothDevice* device,
  325. bool is_now_connected) {
  326. if (!is_now_connected) {
  327. RemoveBluetoothBattery(device->GetAddress());
  328. return;
  329. }
  330. for (auto type : device->GetAvailableBatteryTypes()) {
  331. absl::optional<device::BluetoothDevice::BatteryInfo> info =
  332. device->GetBatteryInfo(type);
  333. DCHECK(info);
  334. BatteryInfo::ChargeStatus charge_status;
  335. switch (info->charge_state) {
  336. case device::BluetoothDevice::BatteryInfo::ChargeState::kUnknown:
  337. charge_status = BatteryInfo::ChargeStatus::kUnknown;
  338. break;
  339. case device::BluetoothDevice::BatteryInfo::ChargeState::kCharging:
  340. charge_status = BatteryInfo::ChargeStatus::kCharging;
  341. break;
  342. case device::BluetoothDevice::BatteryInfo::ChargeState::kDischarging:
  343. charge_status = BatteryInfo::ChargeStatus::kDischarging;
  344. break;
  345. }
  346. BatteryInfo battery{GetBatteryMapKey(device),
  347. device->GetNameForDisplay(),
  348. info->percentage,
  349. /*battery_report_eligible=*/true,
  350. base::TimeTicks::Now(),
  351. BatteryInfo::PeripheralType::kOther,
  352. charge_status,
  353. device->GetAddress()};
  354. UpdateBattery(battery, /*active_update=*/true);
  355. }
  356. }
  357. // Observing device::BluetoothAdapter
  358. void PeripheralBatteryListener::DeviceRemoved(device::BluetoothAdapter* adapter,
  359. device::BluetoothDevice* device) {
  360. RemoveBluetoothBattery(device->GetAddress());
  361. }
  362. void PeripheralBatteryListener::InitializeOnBluetoothReady(
  363. scoped_refptr<device::BluetoothAdapter> adapter) {
  364. bluetooth_adapter_ = adapter;
  365. CHECK(bluetooth_adapter_);
  366. bluetooth_adapter_->AddObserver(this);
  367. }
  368. void PeripheralBatteryListener::RemoveBluetoothBattery(
  369. const std::string& bluetooth_address) {
  370. auto it = batteries_.find(kBluetoothDeviceIdPrefix +
  371. base::ToLowerASCII(bluetooth_address));
  372. if (it != batteries_.end()) {
  373. NotifyRemovingBattery(it->second);
  374. batteries_.erase(it);
  375. }
  376. }
  377. // Observing ui::DeviceDataManager:
  378. void PeripheralBatteryListener::OnDeviceListsComplete() {
  379. chromeos::PowerManagerClient::Get()->RequestAllPeripheralBatteryUpdate();
  380. }
  381. // Present a charge level and charging/full state based on the prior value. We
  382. // don't try to make an accurate estimate of charge level, as it could be
  383. // completely wrong. We instead assume that charging will always take the
  384. // maxmium amount of time, hold the charge level unchanged, at a max of 99% (not
  385. // fully charged), until the maximum charge time expires. Then it is reported at
  386. // 100% and full. This ensures it will not report full until it is _definitely_
  387. // full, and we don't provide a worse estimate than we were already showing.
  388. void PeripheralBatteryListener::GarageTimerAction(
  389. base::TimeTicks charge_start_time,
  390. absl::optional<uint8_t> start_level) {
  391. if (!synthetic_stylus_garage_peripheral_)
  392. return;
  393. auto it = batteries_.find(kStylusGarageKey);
  394. if (it == batteries_.end()) {
  395. return;
  396. }
  397. BatteryInfo info = it->second;
  398. info.last_update_timestamp = base::TimeTicks::Now();
  399. base::TimeDelta charge_period = base::TimeTicks::Now() - charge_start_time;
  400. int new_level = start_level.has_value() ? *start_level : 1;
  401. if (new_level < 1)
  402. new_level = 1;
  403. if (new_level >= 99)
  404. new_level = 99;
  405. // Consider it fully charged only after the max time has passed.
  406. if (charge_period.InMilliseconds() >= kGaragedStylusChargeTime) {
  407. info.level = 100;
  408. info.charge_status = BatteryInfo::ChargeStatus::kFull;
  409. garage_charge_timer_.Stop();
  410. } else {
  411. info.level = new_level;
  412. info.charge_status = BatteryInfo::ChargeStatus::kCharging;
  413. }
  414. UpdateBattery(info, true);
  415. }
  416. absl::optional<uint8_t> PeripheralBatteryListener::DerateLastChargeLevel() {
  417. BatteryInfo latest_battery;
  418. // Find the battery info with most recent data about the stylus
  419. for (auto it : batteries_) {
  420. if (it.second.type != BatteryInfo::PeripheralType::kStylusViaScreen &&
  421. it.second.type != BatteryInfo::PeripheralType::kStylusViaCharger) {
  422. continue;
  423. }
  424. if (!it.second.last_active_update_timestamp.has_value())
  425. continue;
  426. if (latest_battery.last_active_update_timestamp <
  427. it.second.last_active_update_timestamp) {
  428. latest_battery = it.second;
  429. }
  430. }
  431. // No information available.
  432. if (!latest_battery.level.has_value())
  433. return absl::nullopt;
  434. int level = *latest_battery.level;
  435. // We could do an estimate on charge level assuming a known discharge rate,
  436. // however we cannot prove it is the same stylus, and the operation would be
  437. // clearly incorrect if someone is swapping between two styluses to keep them
  438. // charged. Instead we simply report the last known level, or 99 at max, just
  439. // below full. This is not a correct estimate, but it is a useful value for
  440. // the UX. (99 max means we will never immediately say the stylus is full, and
  441. // using the last reading as minimum means we will never show 'low battery'
  442. // unless it was already the case).
  443. if (!level)
  444. level = 1;
  445. if (level >= 99)
  446. level = 99;
  447. return level;
  448. }
  449. void PeripheralBatteryListener::OnStylusStateChanged(
  450. ui::StylusState stylus_state) {
  451. if (!synthetic_stylus_garage_peripheral_)
  452. return;
  453. if (stylus_state == current_stylus_state_)
  454. return;
  455. auto it = batteries_.find(kStylusGarageKey);
  456. if (it == batteries_.end())
  457. return;
  458. BatteryInfo info = it->second;
  459. if (stylus_state == ui::StylusState::INSERTED) {
  460. // Set charger level from last prior reading, minus the estimated discharge
  461. // amount since the time of that last reading.
  462. info.level = DerateLastChargeLevel();
  463. info.charge_status = info.level >= 100
  464. ? BatteryInfo::ChargeStatus::kFull
  465. : BatteryInfo::ChargeStatus::kCharging;
  466. UpdateBattery(info, true);
  467. if (info.charge_status == BatteryInfo::ChargeStatus::kCharging) {
  468. base::TimeTicks charge_start_time = base::TimeTicks::Now();
  469. garage_charge_timer_.Start(
  470. FROM_HERE, base::Milliseconds(kGarageChargeUpdatePeriod),
  471. base::BindRepeating(&PeripheralBatteryListener::GarageTimerAction,
  472. base::Unretained(this), charge_start_time,
  473. info.level));
  474. } else {
  475. garage_charge_timer_.Stop();
  476. }
  477. } else if (stylus_state == ui::StylusState::REMOVED) {
  478. garage_charge_timer_.Stop();
  479. // We leave the charge level unchanged, it may not be accurate, but
  480. // it will be corrected once the stylus is used on the screen; any
  481. // alternative (revising the estimate, or reverting to the value at
  482. // the beginning of charge, if it wasn't fully charged) would lead to the
  483. // level jumping when the stylus is removed from the garage.
  484. info.charge_status = BatteryInfo::ChargeStatus::kUnknown;
  485. UpdateBattery(info, true);
  486. }
  487. current_stylus_state_ = stylus_state;
  488. }
  489. void PeripheralBatteryListener::UpdateBattery(const BatteryInfo& battery_info,
  490. bool active_update) {
  491. const std::string& map_key = battery_info.key;
  492. auto it = batteries_.find(map_key);
  493. if (it == batteries_.end()) {
  494. batteries_[map_key] = battery_info;
  495. NotifyAddingBattery(batteries_[map_key]);
  496. } else {
  497. BatteryInfo& existing_battery_info = it->second;
  498. // Only some fields should ever change.
  499. DCHECK(existing_battery_info.bluetooth_address == battery_info.bluetooth_address);
  500. DCHECK(existing_battery_info.type == battery_info.type);
  501. existing_battery_info.name = battery_info.name;
  502. // Ignore a null level for stylus charger updates: we want to memorize
  503. // the last known actual value. (The touchscreen controller firmware
  504. // already memorizes this, for that path).
  505. if (battery_info.type != BatteryInfo::PeripheralType::kStylusViaCharger ||
  506. battery_info.level)
  507. existing_battery_info.level = battery_info.level;
  508. existing_battery_info.last_update_timestamp =
  509. battery_info.last_update_timestamp;
  510. existing_battery_info.charge_status = battery_info.charge_status;
  511. existing_battery_info.battery_report_eligible =
  512. battery_info.battery_report_eligible;
  513. }
  514. BatteryInfo& info = batteries_[map_key];
  515. if (active_update) {
  516. info.last_active_update_timestamp = info.last_update_timestamp;
  517. }
  518. NotifyUpdatedBatteryLevel(info);
  519. }
  520. void PeripheralBatteryListener::NotifyAddingBattery(
  521. const BatteryInfo& battery) {
  522. for (auto& obs : observers_)
  523. obs.OnAddingBattery(battery);
  524. }
  525. void PeripheralBatteryListener::NotifyRemovingBattery(
  526. const BatteryInfo& battery) {
  527. for (auto& obs : observers_)
  528. obs.OnRemovingBattery(battery);
  529. }
  530. void PeripheralBatteryListener::NotifyUpdatedBatteryLevel(
  531. const BatteryInfo& battery) {
  532. for (auto& obs : observers_)
  533. obs.OnUpdatedBatteryLevel(battery);
  534. }
  535. void PeripheralBatteryListener::AddObserver(Observer* observer) {
  536. observers_.AddObserver(observer);
  537. // As possible latecomer, introduce observer to batteries that already exist.
  538. for (auto it : batteries_) {
  539. observer->OnAddingBattery(it.second);
  540. observer->OnUpdatedBatteryLevel(it.second);
  541. }
  542. }
  543. void PeripheralBatteryListener::RemoveObserver(Observer* observer) {
  544. observers_.RemoveObserver(observer);
  545. }
  546. bool PeripheralBatteryListener::HasObserver(const Observer* observer) const {
  547. return observers_.HasObserver(observer);
  548. }
  549. } // namespace ash