power_status.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // Copyright (c) 2013 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/power_status.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/public/cpp/power_utils.h"
  9. #include "ash/resources/vector_icons/vector_icons.h"
  10. #include "ash/strings/grit/ash_strings.h"
  11. #include "ash/system/power/battery_image_source.h"
  12. #include "base/i18n/number_formatting.h"
  13. #include "base/i18n/time_formatting.h"
  14. #include "base/logging.h"
  15. #include "base/memory/ptr_util.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "chromeos/dbus/power/power_manager_client.h"
  19. #include "ui/base/l10n/l10n_util.h"
  20. #include "ui/base/l10n/time_format.h"
  21. #include "ui/display/display.h"
  22. #include "ui/display/screen.h"
  23. #include "ui/gfx/color_palette.h"
  24. #include "ui/gfx/image/image.h"
  25. namespace ash {
  26. namespace {
  27. static PowerStatus* g_power_status = nullptr;
  28. std::u16string GetBatteryTimeAccessibilityString(int hour, int min) {
  29. DCHECK(hour || min);
  30. if (hour && !min) {
  31. return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
  32. ui::TimeFormat::LENGTH_LONG,
  33. base::Hours(hour));
  34. }
  35. if (min && !hour) {
  36. return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
  37. ui::TimeFormat::LENGTH_LONG,
  38. base::Minutes(min));
  39. }
  40. return l10n_util::GetStringFUTF16(
  41. IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE,
  42. ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
  43. ui::TimeFormat::LENGTH_LONG, base::Hours(hour)),
  44. ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
  45. ui::TimeFormat::LENGTH_LONG, base::Minutes(min)));
  46. }
  47. int PowerSourceToMessageID(
  48. const power_manager::PowerSupplyProperties_PowerSource& source) {
  49. switch (source.port()) {
  50. case power_manager::PowerSupplyProperties_PowerSource_Port_UNKNOWN:
  51. return IDS_ASH_POWER_SOURCE_PORT_UNKNOWN;
  52. case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT:
  53. return IDS_ASH_POWER_SOURCE_PORT_LEFT;
  54. case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT:
  55. return IDS_ASH_POWER_SOURCE_PORT_RIGHT;
  56. case power_manager::PowerSupplyProperties_PowerSource_Port_BACK:
  57. return IDS_ASH_POWER_SOURCE_PORT_BACK;
  58. case power_manager::PowerSupplyProperties_PowerSource_Port_FRONT:
  59. return IDS_ASH_POWER_SOURCE_PORT_FRONT;
  60. case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_FRONT:
  61. return IDS_ASH_POWER_SOURCE_PORT_LEFT_FRONT;
  62. case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_BACK:
  63. return IDS_ASH_POWER_SOURCE_PORT_LEFT_BACK;
  64. case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_FRONT:
  65. return IDS_ASH_POWER_SOURCE_PORT_RIGHT_FRONT;
  66. case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_BACK:
  67. return IDS_ASH_POWER_SOURCE_PORT_RIGHT_BACK;
  68. case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_LEFT:
  69. return IDS_ASH_POWER_SOURCE_PORT_BACK_LEFT;
  70. case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_RIGHT:
  71. return IDS_ASH_POWER_SOURCE_PORT_BACK_RIGHT;
  72. }
  73. NOTREACHED();
  74. return 0;
  75. }
  76. } // namespace
  77. bool PowerStatus::BatteryImageInfo::ApproximatelyEqual(
  78. const BatteryImageInfo& o) const {
  79. // 100% is distinct from all else.
  80. if ((charge_percent != o.charge_percent) &&
  81. (charge_percent == 100 || o.charge_percent == 100)) {
  82. return false;
  83. }
  84. // Otherwise, consider close values such as 42% and 45% as about the same.
  85. return icon_badge == o.icon_badge && alert_if_low == o.alert_if_low &&
  86. std::abs(charge_percent - o.charge_percent) < 5;
  87. }
  88. const int PowerStatus::kMaxBatteryTimeToDisplaySec = 24 * 60 * 60;
  89. const double PowerStatus::kCriticalBatteryChargePercentage = 5;
  90. // static
  91. void PowerStatus::Initialize() {
  92. CHECK(!g_power_status);
  93. g_power_status = new PowerStatus();
  94. }
  95. // static
  96. void PowerStatus::Shutdown() {
  97. CHECK(g_power_status);
  98. delete g_power_status;
  99. g_power_status = nullptr;
  100. }
  101. // static
  102. bool PowerStatus::IsInitialized() {
  103. return g_power_status != nullptr;
  104. }
  105. // static
  106. PowerStatus* PowerStatus::Get() {
  107. CHECK(g_power_status) << "PowerStatus::Get() called before Initialize().";
  108. return g_power_status;
  109. }
  110. void PowerStatus::AddObserver(Observer* observer) {
  111. DCHECK(observer);
  112. observers_.AddObserver(observer);
  113. }
  114. void PowerStatus::RemoveObserver(Observer* observer) {
  115. DCHECK(observer);
  116. observers_.RemoveObserver(observer);
  117. }
  118. void PowerStatus::RequestStatusUpdate() {
  119. chromeos::PowerManagerClient::Get()->RequestStatusUpdate();
  120. }
  121. bool PowerStatus::IsBatteryPresent() const {
  122. return proto_.battery_state() !=
  123. power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
  124. }
  125. bool PowerStatus::IsBatteryFull() const {
  126. return proto_.battery_state() ==
  127. power_manager::PowerSupplyProperties_BatteryState_FULL;
  128. }
  129. bool PowerStatus::IsBatteryCharging() const {
  130. return proto_.battery_state() ==
  131. power_manager::PowerSupplyProperties_BatteryState_CHARGING;
  132. }
  133. bool PowerStatus::IsBatteryDischargingOnLinePower() const {
  134. return IsLinePowerConnected() &&
  135. proto_.battery_state() ==
  136. power_manager::PowerSupplyProperties_BatteryState_DISCHARGING;
  137. }
  138. double PowerStatus::GetBatteryPercent() const {
  139. return proto_.battery_percent();
  140. }
  141. int PowerStatus::GetRoundedBatteryPercent() const {
  142. return power_utils::GetRoundedBatteryPercent(GetBatteryPercent());
  143. }
  144. bool PowerStatus::IsBatteryTimeBeingCalculated() const {
  145. return proto_.is_calculating_battery_time();
  146. }
  147. absl::optional<base::TimeDelta> PowerStatus::GetBatteryTimeToEmpty() const {
  148. // powerd omits the field if no battery is present and sends -1 if it couldn't
  149. // compute a reasonable estimate.
  150. if (!proto_.has_battery_time_to_empty_sec() ||
  151. proto_.battery_time_to_empty_sec() < 0) {
  152. return absl::nullopt;
  153. }
  154. return base::Seconds(proto_.battery_time_to_empty_sec());
  155. }
  156. absl::optional<base::TimeDelta> PowerStatus::GetBatteryTimeToFull() const {
  157. // powerd omits the field if no battery is present and sends -1 if it couldn't
  158. // compute a reasonable estimate.
  159. if (!proto_.has_battery_time_to_full_sec() ||
  160. proto_.battery_time_to_full_sec() < 0) {
  161. return absl::nullopt;
  162. }
  163. return base::Seconds(proto_.battery_time_to_full_sec());
  164. }
  165. bool PowerStatus::IsLinePowerConnected() const {
  166. return proto_.external_power() !=
  167. power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
  168. }
  169. bool PowerStatus::IsMainsChargerConnected() const {
  170. return proto_.external_power() ==
  171. power_manager::PowerSupplyProperties_ExternalPower_AC;
  172. }
  173. bool PowerStatus::IsUsbChargerConnected() const {
  174. return proto_.external_power() ==
  175. power_manager::PowerSupplyProperties_ExternalPower_USB;
  176. }
  177. bool PowerStatus::SupportsDualRoleDevices() const {
  178. return proto_.supports_dual_role_devices();
  179. }
  180. bool PowerStatus::HasDualRoleDevices() const {
  181. if (!SupportsDualRoleDevices())
  182. return false;
  183. for (int i = 0; i < proto_.available_external_power_source_size(); i++) {
  184. if (!proto_.available_external_power_source(i).active_by_default())
  185. return true;
  186. }
  187. return false;
  188. }
  189. std::vector<PowerStatus::PowerSource> PowerStatus::GetPowerSources() const {
  190. std::vector<PowerSource> sources;
  191. for (int i = 0; i < proto_.available_external_power_source_size(); i++) {
  192. const auto& source = proto_.available_external_power_source(i);
  193. sources.push_back(
  194. {source.id(),
  195. source.active_by_default() ? DEDICATED_CHARGER : DUAL_ROLE_USB,
  196. PowerSourceToMessageID(source)});
  197. }
  198. return sources;
  199. }
  200. std::string PowerStatus::GetCurrentPowerSourceID() const {
  201. return proto_.external_power_source_id();
  202. }
  203. PowerStatus::BatteryImageInfo PowerStatus::GetBatteryImageInfo() const {
  204. BatteryImageInfo info;
  205. CalculateBatteryImageInfo(&info);
  206. return info;
  207. }
  208. void PowerStatus::CalculateBatteryImageInfo(BatteryImageInfo* info) const {
  209. info->alert_if_low = !IsLinePowerConnected();
  210. if (!IsUsbChargerConnected() && !IsBatteryPresent()) {
  211. info->icon_badge = &kUnifiedMenuBatteryXIcon;
  212. if (features::IsDarkLightModeEnabled()) {
  213. info->badge_outline = &kUnifiedMenuBatteryXOutlineMaskIcon;
  214. } else {
  215. info->badge_outline = &kUnifiedMenuBatteryXOutlineIcon;
  216. }
  217. info->charge_percent = 0;
  218. return;
  219. }
  220. if (IsUsbChargerConnected()) {
  221. info->icon_badge = &kUnifiedMenuBatteryUnreliableIcon;
  222. if (features::IsDarkLightModeEnabled()) {
  223. info->badge_outline = &kUnifiedMenuBatteryUnreliableOutlineMaskIcon;
  224. } else {
  225. info->badge_outline = &kUnifiedMenuBatteryUnreliableOutlineIcon;
  226. }
  227. } else if (IsLinePowerConnected()) {
  228. info->icon_badge = &kUnifiedMenuBatteryBoltIcon;
  229. if (features::IsDarkLightModeEnabled()) {
  230. info->badge_outline = &kUnifiedMenuBatteryBoltOutlineMaskIcon;
  231. } else {
  232. info->badge_outline = &kUnifiedMenuBatteryBoltOutlineIcon;
  233. }
  234. } else {
  235. info->icon_badge = nullptr;
  236. info->badge_outline = nullptr;
  237. }
  238. info->charge_percent = GetBatteryPercent();
  239. // Use an alert badge if the battery is critically low and does not already
  240. // have a badge assigned.
  241. if (GetBatteryPercent() < kCriticalBatteryChargePercentage &&
  242. !info->icon_badge) {
  243. info->icon_badge = &kUnifiedMenuBatteryAlertIcon;
  244. if (features::IsDarkLightModeEnabled()) {
  245. info->badge_outline = &kUnifiedMenuBatteryAlertOutlineMaskIcon;
  246. } else {
  247. info->badge_outline = &kUnifiedMenuBatteryAlertOutlineIcon;
  248. }
  249. }
  250. }
  251. // static
  252. gfx::ImageSkia PowerStatus::GetBatteryImage(
  253. const BatteryImageInfo& info,
  254. int height,
  255. SkColor bg_color,
  256. SkColor fg_color,
  257. absl::optional<SkColor> badge_color) {
  258. auto* source = new BatteryImageSource(info, height, bg_color, fg_color,
  259. std::move(badge_color));
  260. return gfx::ImageSkia(base::WrapUnique(source), source->size());
  261. }
  262. std::u16string PowerStatus::GetAccessibleNameString(
  263. bool full_description) const {
  264. if (IsBatteryFull()) {
  265. return l10n_util::GetStringUTF16(
  266. IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE);
  267. }
  268. std::u16string battery_percentage_accessible = l10n_util::GetStringFUTF16(
  269. IsBatteryCharging()
  270. ? IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE
  271. : IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE,
  272. base::NumberToString16(GetRoundedBatteryPercent()));
  273. if (!full_description)
  274. return battery_percentage_accessible;
  275. std::u16string battery_time_accessible = std::u16string();
  276. const absl::optional<base::TimeDelta> time =
  277. IsBatteryCharging() ? GetBatteryTimeToFull() : GetBatteryTimeToEmpty();
  278. if (IsUsbChargerConnected()) {
  279. battery_time_accessible = l10n_util::GetStringUTF16(
  280. IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
  281. } else if (IsBatteryTimeBeingCalculated()) {
  282. battery_time_accessible = l10n_util::GetStringUTF16(
  283. IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE);
  284. } else if (time && power_utils::ShouldDisplayBatteryTime(*time) &&
  285. !IsBatteryDischargingOnLinePower()) {
  286. int hour = 0, min = 0;
  287. power_utils::SplitTimeIntoHoursAndMinutes(*time, &hour, &min);
  288. std::u16string minute = min < 10 ? u"0" + base::NumberToString16(min)
  289. : base::NumberToString16(min);
  290. battery_time_accessible = l10n_util::GetStringFUTF16(
  291. IsBatteryCharging()
  292. ? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE
  293. : IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE,
  294. GetBatteryTimeAccessibilityString(hour, min));
  295. }
  296. return battery_time_accessible.empty()
  297. ? battery_percentage_accessible
  298. : battery_percentage_accessible + u" " + battery_time_accessible;
  299. }
  300. std::pair<std::u16string, std::u16string> PowerStatus::GetStatusStrings()
  301. const {
  302. std::u16string percentage;
  303. std::u16string status;
  304. if (IsBatteryFull()) {
  305. status = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BATTERY_FULL);
  306. } else {
  307. percentage = base::FormatPercent(GetRoundedBatteryPercent());
  308. if (IsUsbChargerConnected()) {
  309. status = l10n_util::GetStringUTF16(
  310. IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE);
  311. } else if (IsBatteryTimeBeingCalculated()) {
  312. status =
  313. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING);
  314. } else {
  315. absl::optional<base::TimeDelta> time = IsBatteryCharging()
  316. ? GetBatteryTimeToFull()
  317. : GetBatteryTimeToEmpty();
  318. if (time && power_utils::ShouldDisplayBatteryTime(*time) &&
  319. !IsBatteryDischargingOnLinePower()) {
  320. std::u16string duration;
  321. if (!base::TimeDurationFormat(*time, base::DURATION_WIDTH_NUMERIC,
  322. &duration))
  323. LOG(ERROR) << "Failed to format duration " << *time;
  324. status = l10n_util::GetStringFUTF16(
  325. IsBatteryCharging()
  326. ? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT
  327. : IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT,
  328. duration);
  329. }
  330. }
  331. }
  332. return std::make_pair(percentage, status);
  333. }
  334. std::u16string PowerStatus::GetInlinedStatusString() const {
  335. auto [percentage_text, status_text] = GetStatusStrings();
  336. if (!percentage_text.empty() && !status_text.empty()) {
  337. return percentage_text +
  338. l10n_util::GetStringUTF16(
  339. IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR) +
  340. status_text;
  341. } else if (!percentage_text.empty()) {
  342. return percentage_text;
  343. } else {
  344. return status_text;
  345. }
  346. }
  347. double PowerStatus::GetPreferredMinimumPower() const {
  348. return proto_.preferred_minimum_external_power();
  349. }
  350. PowerStatus::PowerStatus() {
  351. chromeos::PowerManagerClient::Get()->AddObserver(this);
  352. chromeos::PowerManagerClient::Get()->RequestStatusUpdate();
  353. }
  354. PowerStatus::~PowerStatus() {
  355. chromeos::PowerManagerClient::Get()->RemoveObserver(this);
  356. }
  357. void PowerStatus::SetProtoForTesting(
  358. const power_manager::PowerSupplyProperties& proto) {
  359. proto_ = proto;
  360. }
  361. void PowerStatus::PowerChanged(
  362. const power_manager::PowerSupplyProperties& proto) {
  363. proto_ = proto;
  364. for (auto& observer : observers_)
  365. observer.OnPowerStatusChanged();
  366. }
  367. } // namespace ash