blob: f61122e03aeea64ab5ecac2e36cc0a673660fe1b [file] [log] [blame]
// Copyright 2017 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_ML_IDLE_EVENT_NOTIFIER_H_
#define CHROME_BROWSER_CHROMEOS_POWER_ML_IDLE_EVENT_NOTIFIER_H_
#include <memory>
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "chromeos/dbus/power_manager_client.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/viz/public/interfaces/compositing/video_detector_observer.mojom.h"
#include "ui/base/user_activity/user_activity_detector.h"
#include "ui/base/user_activity/user_activity_observer.h"
namespace base {
class Clock;
}
namespace chromeos {
namespace power {
namespace ml {
class BootClock;
class RecentEventsCounter;
// This is time since midnight in the local time zone and may move back or
// forward when DST starts or stops.
using TimeOfDay = base::TimeDelta;
// IdleEventNotifier listens to signals and notifies its observers when
// ScreenDimImminent is received from PowerManagerClient. This generates an idle
// event.
class IdleEventNotifier : public PowerManagerClient::Observer,
public ui::UserActivityObserver,
public viz::mojom::VideoDetectorObserver {
public:
// If suspend duration is greater than this, we reset timestamps used to calc
// |ActivityData::recent_time_active|. We also merge video-playing sessions
// that have a pause shorter than this.
static constexpr base::TimeDelta kIdleDelay =
base::TimeDelta::FromSeconds(30);
// Count number of key, mouse and touch events in the past hour.
static constexpr base::TimeDelta kUserInputEventsDuration =
base::TimeDelta::FromMinutes(60);
// Granularity of input events is per minute.
static constexpr int kNumUserInputEventsBuckets =
kUserInputEventsDuration / base::TimeDelta::FromMinutes(1);
struct ActivityData {
ActivityData();
ActivityData(const ActivityData& input_data);
UserActivityEvent_Features_DayOfWeek last_activity_day =
UserActivityEvent_Features_DayOfWeek_SUN;
// The local time of the last activity before an idle event occurs.
TimeOfDay last_activity_time_of_day;
// Last user activity time of the sequence of activities ending in the last
// activity. It could be different from |last_activity_time_of_day|
// if the last activity is not a user activity (e.g. video). It is unset if
// there is no user activity before the idle event is fired.
base::Optional<TimeOfDay> last_user_activity_time_of_day;
// Duration of activity up to the last activity.
base::TimeDelta recent_time_active;
// Duration from the last key/mouse/touch to the time when idle event is
// generated. It is unset if there is no key/mouse/touch activity before
// the idle event.
base::Optional<base::TimeDelta> time_since_last_key;
base::Optional<base::TimeDelta> time_since_last_mouse;
base::Optional<base::TimeDelta> time_since_last_touch;
// How long recent video has been playing.
base::TimeDelta video_playing_time;
// Duration from when video ended. It is unset if video did not play
// (|video_playing_time| = 0).
base::Optional<base::TimeDelta> time_since_video_ended;
int key_events_in_last_hour = 0;
int mouse_events_in_last_hour = 0;
int touch_events_in_last_hour = 0;
};
class Observer {
public:
// Called when an idle event is observed.
virtual void OnIdleEventObserved(const ActivityData& activity_data) = 0;
protected:
virtual ~Observer() {}
};
IdleEventNotifier(PowerManagerClient* power_client,
ui::UserActivityDetector* detector,
viz::mojom::VideoDetectorObserverRequest request);
~IdleEventNotifier() override;
// Set test clock so that we can check activity time.
void SetClockForTesting(scoped_refptr<base::SequencedTaskRunner> task_runner,
base::Clock* test_clock,
std::unique_ptr<BootClock> test_boot_clock);
// Adds or removes an observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// chromeos::PowerManagerClient::Observer overrides:
void LidEventReceived(chromeos::PowerManagerClient::LidState state,
const base::TimeTicks& timestamp) override;
void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
void ScreenDimImminent() override;
void SuspendDone(const base::TimeDelta& sleep_duration) override;
// ui::UserActivityObserver overrides:
void OnUserActivity(const ui::Event* event) override;
// viz::mojom::VideoDetectorObserver overrides:
void OnVideoActivityStarted() override;
void OnVideoActivityEnded() override;
private:
FRIEND_TEST_ALL_PREFIXES(IdleEventNotifierTest, CheckInitialValues);
friend class IdleEventNotifierTest;
enum class ActivityType {
USER_OTHER, // All other user-related activities.
KEY,
MOUSE,
VIDEO,
TOUCH
};
struct ActivityDataInternal;
ActivityData ConvertActivityData(
const ActivityDataInternal& internal_data) const;
// Updates all activity-related timestamps.
void UpdateActivityData(ActivityType type);
// Clears timestamps used to calculate |ActivityData::recent_time_active| so
// that its duration is recalculated after ScreenDimImminent is received or
// when suspend duration is longer than kIdleDelay.
// Also clears timestamps for video playing so that duration of video playing
// will be recalculated.
void ResetTimestampsForRecentActivity();
// It is base::DefaultClock, but will be set to a mock clock for tests.
base::Clock* clock_;
// It is RealBootClock, but will be set to FakeBootClock for tests.
std::unique_ptr<BootClock> boot_clock_;
ScopedObserver<chromeos::PowerManagerClient,
chromeos::PowerManagerClient::Observer>
power_manager_client_observer_;
ScopedObserver<ui::UserActivityDetector, ui::UserActivityObserver>
user_activity_observer_;
// Last-received external power state. Changes are treated as user activity.
base::Optional<power_manager::PowerSupplyProperties_ExternalPower>
external_power_;
base::ObserverList<Observer>::Unchecked observers_;
// Holds activity timestamps while we monitor for idle events. It will be
// converted to an ActivityData when an idle event is sent out.
std::unique_ptr<ActivityDataInternal> internal_data_;
// Whether video is playing.
bool video_playing_ = false;
mojo::Binding<viz::mojom::VideoDetectorObserver> binding_;
std::unique_ptr<RecentEventsCounter> key_counter_;
std::unique_ptr<RecentEventsCounter> mouse_counter_;
std::unique_ptr<RecentEventsCounter> touch_counter_;
DISALLOW_COPY_AND_ASSIGN(IdleEventNotifier);
};
} // namespace ml
} // namespace power
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_POWER_ML_IDLE_EVENT_NOTIFIER_H_