blob: c292ea6a3989466687e94590d5a9346619fddb50 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_POWER_POWER_NOTIFICATION_CONTROLLER_H_
#define ASH_SYSTEM_POWER_POWER_NOTIFICATION_CONTROLLER_H_
#include <memory>
#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "ash/shell_observer.h"
#include "ash/system/power/battery_saver_controller.h"
#include "ash/system/power/power_status.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chromeos/dbus/power/power_manager_client.h"
namespace message_center {
class MessageCenter;
} // namespace message_center
namespace ash {
class BatteryNotification;
class DualRoleNotification;
// Controller class to manage power/battery notifications.
class ASH_EXPORT PowerNotificationController
: public PowerStatus::Observer,
public chromeos::PowerManagerClient::Observer,
public ash::ShellObserver {
public:
enum NotificationState {
NOTIFICATION_NONE,
// Low battery charge, different battery saver notification behavior.
// Note: When battery saver is not available, both of these act like the
// original low power notification.
NOTIFICATION_BSM_ENABLING_AT_THRESHOLD,
NOTIFICATION_BSM_THRESHOLD_OPT_IN,
NOTIFICATION_GENERIC_LOW_POWER,
// Critically low battery charge.
NOTIFICATION_CRITICAL,
};
enum CriticalNotificationOutcome {
// The device crashes, it includes the case when the battery is empty and
// powerd does not have time to perform a graceful shutdown.
Crashed = 1,
// The device automatically shut down due to a low battery.
LowBatteryShutdown = 2,
// The critical notification is shown, its count should be greater than or
// equal to the sum of all other outcomes.
NotificationShown = 0,
// The device is connected to a power source.
PluggedIn = 3,
// The device enters a suspended state.
Suspended = 4,
// The device is shut down gracefully by user.
UserShutdown = 5,
kMaxValue = UserShutdown,
};
// Time-based notification thresholds when on battery power.
static constexpr int kCriticalMinutes = 5;
static constexpr int kLowPowerMinutes = 15;
static constexpr int kNoWarningMinutes = 30;
// Percentage-based notification thresholds when using a low-power charger.
static constexpr int kCriticalPercentage = 5;
static constexpr int kLowPowerPercentage = 10;
static constexpr int kNoWarningPercentage = 15;
explicit PowerNotificationController(
message_center::MessageCenter* message_center);
PowerNotificationController(const PowerNotificationController&) = delete;
PowerNotificationController& operator=(const PowerNotificationController&) =
delete;
~PowerNotificationController() override;
// static:
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
void NotifyUsbNotificationClosedByUser();
void SetUserOptStatus(bool status);
double GetLowPowerPercentage() const { return low_power_percentage_; }
double GetCriticalPowerPercentage() const { return critical_percentage_; }
double GetNoWarningPercentage() const { return no_warning_percentage_; }
NotificationState GetNotificationState() const { return notification_state_; }
private:
FRIEND_TEST_ALL_PREFIXES(PowerNotificationControllerTest,
MaybeShowUsbChargerNotification);
FRIEND_TEST_ALL_PREFIXES(PowerNotificationControllerTest,
UpdateNotificationState);
friend class PowerNotificationControllerTest;
friend class BatteryNotificationTest;
// Overridden from PowerStatus::Observer.
void OnPowerStatusChanged() override;
// Overridden from PowerManagerClient::Observer:
void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
void ShutdownRequested(power_manager::RequestShutdownReason reason) override;
void RestartRequested(power_manager::RequestRestartReason reason) override;
// Overridden from ash::ShellObserver:
void OnShellDestroying() override;
// Shows a notification that a low-power USB charger has been connected.
// Returns true if a notification was shown or explicitly hidden.
bool MaybeShowUsbChargerNotification();
// Shows a notification when dual-role devices are connected.
void MaybeShowDualRoleNotification();
// Records the outcome of a critical notification.
void MaybeRecordCriticalNotificationOutcome(
PowerNotificationController::CriticalNotificationOutcome outcome,
base::TimeDelta duration);
// Determines whether a Battery Saver Notification should be shown. Returns
// true if a notification should be shown, or nullopt if none of the bsm
// branches were triggered.
std::optional<bool> HandleBatterySaverNotifications();
// Sets |notification_state_|. Returns true if a notification should be shown.
bool UpdateNotificationState();
bool UpdateNotificationStateForRemainingTime();
bool UpdateNotificationStateForRemainingPercentage();
bool UpdateNotificationStateForRemainingPercentageBatterySaver();
// Whether the device is plugged in during a critical battery state.
bool PluggedInCriticalState();
// Start a timer and update the kCriticalStateDuration every 15 seconds.
void StartPeriodicUpdate();
void UpdateCriticalNotificationDurationPrefs();
// Reset the timestamp related to critical notification.
void ResetCriticalNotificationTimestamp();
static const char kUsbNotificationId[];
raw_ptr<PrefService> local_state_; // Unowned.
const raw_ptr<message_center::MessageCenter> message_center_; // Unowned.
std::unique_ptr<BatteryNotification> battery_notification_;
std::unique_ptr<DualRoleNotification> dual_role_notification_;
NotificationState notification_state_ = NOTIFICATION_NONE;
// Was the battery full the last time OnPowerStatusChanged() was called?
bool battery_was_full_ = false;
// Was a USB charger connected the last time OnPowerStatusChanged() was
// called?
bool usb_charger_was_connected_ = false;
// Was line power connected the last time onPowerStatusChanged() was called?
bool line_power_was_connected_ = false;
// Was the battery in critical state the last time onPowerStatusChanged() was
// called?
bool was_in_critical_state_ = false;
// The remaining battery time the last time OnPowerStatusChanged() was called.
// This value is utilized to determine the remaining battery time at the
// moment the charger is connected.
std::optional<base::TimeDelta> remaining_time_to_empty_from_critical_state_;
// Has the user already dismissed a low-power notification? Should be set
// back to false when all power sources are disconnected.
bool usb_notification_dismissed_ = false;
// Has the battery saver threshold been crossed? Also gets reset to false when
// an AC charger is plugged in.
bool battery_saver_triggered_ = false;
// User opt status.
bool user_opt_status_ = false;
const double battery_saver_activation_charge_percent_;
// Percentage-based notification thresholds for battery saver.
// TODO(mwoj): Replace the static constexpr once data is collected from the
// experiment.
const int critical_percentage_;
const int low_power_percentage_;
const int no_warning_percentage_;
// After critical notification shows, trigger
// `UpdateCriticalNotificationDurationPrefs` periodically.
base::RepeatingTimer timer_;
// The time at which a critical notification is shown.
base::TimeTicks critical_notification_shown_time_ = base::TimeTicks();
// The observation on `ash::Shell`.
base::ScopedObservation<ash::Shell, ash::ShellObserver> shell_observation_{
this};
};
} // namespace ash
#endif // ASH_SYSTEM_POWER_POWER_NOTIFICATION_CONTROLLER_H_