Commit e6beeda6 authored by Thanh Nguyen's avatar Thanh Nguyen Committed by Commit Bot

[smart-charging] Collect last charge features

Adds code to collect last charge related features.

Bug: 1028853
Change-Id: I2b152139db0777acbd40534128ed8c92004799b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1994035
Commit-Queue: Thanh Nguyen <thanhdng@chromium.org>
Reviewed-by: default avatarJia Meng <jiameng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#731862}
parent 1e787165
......@@ -4,9 +4,24 @@
#include "chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h"
#include <memory>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/location.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task_runner_util.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/constants/devicetype.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "ui/aura/env.h"
......@@ -16,6 +31,8 @@ namespace chromeos {
namespace power {
namespace {
constexpr int kBucketSize = 15;
// Interval at which data should be logged.
constexpr base::TimeDelta kLoggingInterval = base::TimeDelta::FromMinutes(30);
......@@ -27,11 +44,89 @@ constexpr base::TimeDelta kUserActivityDuration =
// Granularity of input events is per minute.
constexpr int kNumUserInputEventsBuckets =
kUserActivityDuration / base::TimeDelta::FromMinutes(1);
constexpr char kSavedFileName[] = "past_charging_events.pb";
constexpr char kSavedDir[] = "smartcharging";
// Given a proto and file path, writes to disk and logs the error(if any).
void WriteProtoToDisk(const PastChargingEvents& proto,
const base::FilePath& file_path) {
std::string proto_string;
if (!proto.SerializeToString(&proto_string)) {
// TODO(crbug.com/1028853): adds a UMA log here.
return;
}
bool write_result;
{
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
write_result = base::ImportantFileWriter::WriteFileAtomically(
file_path, proto_string.data(), "SmartCharging");
}
if (!write_result) {
// TODO(crbug.com/1028853): adds a UMA log here.
return;
}
// TODO(crbug.com/1028853): adds a UMA log here.
}
// Reads a proto from a file path.
std::unique_ptr<PastChargingEvents> ReadProto(const base::FilePath& file_path) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
std::string proto_str;
if (!base::ReadFileToString(file_path, &proto_str)) {
// TODO(crbug.com/1028853): adds a UMA log here.
return nullptr;
}
auto proto = std::make_unique<PastChargingEvents>();
if (!proto->ParseFromString(proto_str)) {
// TODO(crbug.com/1028853): adds a UMA log here.
return nullptr;
}
// TODO(crbug.com/1028853): adds a UMA log here.
return proto;
}
// If |create_new_path| is true, try to create new path and return true if
// success.
// If |create_new_path| is false, try to get the path and return true if
// sucesss.
bool GetPathSuccess(base::FilePath* file_path, bool create_new_path) {
if (!ProfileManager::GetPrimaryUserProfile()) {
// TODO(crbug.com/1028853): adds a UMA log here.
return false;
}
const base::FilePath path =
ProfileManager::GetPrimaryUserProfile()->GetPath().AppendASCII(kSavedDir);
if (create_new_path) {
if (!base::DirectoryExists(path) && !base::CreateDirectory(path)) {
// TODO(crbug.com/1028853): adds a UMA log here.
return false;
}
} else if (!base::PathExists(path.AppendASCII(kSavedFileName))) {
// TODO(crbug.com/1028853): adds a UMA log here.
return false;
}
// TODO(crbug.com/1028853): adds a UMA log here.
*file_path = path.AppendASCII(kSavedFileName);
return true;
}
// Checks if an event is a halt (shutdown/suspend) event.
bool IsHaltEvent(const PastEvent& event) {
return event.reason() == UserChargingEvent::Event::SHUTDOWN ||
event.reason() == UserChargingEvent::Event::SUSPEND;
}
} // namespace
SmartChargingManager::SmartChargingManager(
ui::UserActivityDetector* detector,
mojo::PendingReceiver<viz::mojom::VideoDetectorObserver> receiver,
session_manager::SessionManager* session_manager,
std::unique_ptr<base::RepeatingTimer> periodic_timer)
: periodic_timer_(std::move(periodic_timer)),
receiver_(this, std::move(receiver)),
......@@ -50,8 +145,13 @@ SmartChargingManager::SmartChargingManager(
ukm_logger_(std::make_unique<SmartChargingUkmLogger>()) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(detector);
DCHECK(session_manager);
user_activity_observer_.Add(detector);
power_manager_client_observer_.Add(chromeos::PowerManagerClient::Get());
session_manager_observer_.Add(session_manager);
blocking_task_runner_ = base::CreateSequencedTaskRunner(
{base::ThreadPool(), base::TaskPriority::BEST_EFFORT, base::MayBlock(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
}
SmartChargingManager::~SmartChargingManager() = default;
......@@ -70,6 +170,7 @@ std::unique_ptr<SmartChargingManager> SmartChargingManager::CreateInstance() {
std::unique_ptr<SmartChargingManager> screen_brightness_manager =
std::make_unique<SmartChargingManager>(
detector, video_observer.InitWithNewPipeAndPassReceiver(),
session_manager::SessionManager::Get(),
std::make_unique<base::RepeatingTimer>());
aura::Env::GetInstance()
......@@ -201,6 +302,17 @@ void SmartChargingManager::OnVideoActivityEnded() {
is_video_playing_ = false;
}
void SmartChargingManager::OnUserSessionStarted(bool /* is_primary_user */) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The first sign-in user is the primary user, hence if |OnUserSessionStarted|
// is called, the primary user profile should have been created. We will
// ignore |is_primary_user|.
if (loaded_from_disk_)
return;
MaybeLoadFromDisk();
}
void SmartChargingManager::PopulateUserChargingEventProto(
UserChargingEvent* proto) {
auto& features = *proto->mutable_features();
......@@ -229,8 +341,9 @@ void SmartChargingManager::PopulateUserChargingEventProto(
base::Time::Exploded now_exploded;
now.LocalExplode(&now_exploded);
features.set_time_of_the_day(ukm::GetExponentialBucketMinForCounts1000(
now_exploded.hour * 60 + now_exploded.minute));
features.set_time_of_the_day(ukm::GetLinearBucketMin(
static_cast<int64_t>(now_exploded.hour * 60 + now_exploded.minute),
kBucketSize));
features.set_day_of_week(static_cast<UserChargingEvent::Features::DayOfWeek>(
now_exploded.day_of_week));
features.set_day_of_month(now_exploded.day_of_month);
......@@ -247,10 +360,41 @@ void SmartChargingManager::PopulateUserChargingEventProto(
} else {
features.set_device_mode(UserChargingEvent::Features::UNKNOWN_MODE);
}
// Last charge related features. This logic relies on the fact that
// there will be at most one halt event because of |UpdatePastEvents()|.
bool halt_from_last_charge = false;
for (const auto& event : past_events_) {
if (IsHaltEvent(event)) {
halt_from_last_charge = true;
break;
}
}
features.set_halt_from_last_charge(halt_from_last_charge);
PastEvent last_charge_plugged_in;
PastEvent last_charge_unplugged;
std::tie(last_charge_plugged_in, last_charge_unplugged) =
GetLastChargeEvents();
if (!last_charge_plugged_in.has_time() || !last_charge_unplugged.has_time())
return;
features.set_time_since_last_charge(ukm::GetExponentialBucketMinForCounts1000(
now.ToDeltaSinceWindowsEpoch().InMinutes() -
last_charge_unplugged.time()));
features.set_duration_of_last_charge(
ukm::GetExponentialBucketMinForCounts1000(last_charge_unplugged.time() -
last_charge_plugged_in.time()));
features.set_battery_percentage_before_last_charge(
last_charge_plugged_in.battery_percent());
features.set_battery_percentage_of_last_charge(
last_charge_unplugged.battery_percent());
features.set_timezone_difference_from_last_charge(
(now.UTCMidnight() - now.LocalMidnight()).InHours() -
last_charge_unplugged.timezone());
}
void SmartChargingManager::LogEvent(
const UserChargingEvent::Event::Reason& reason) {
void SmartChargingManager::LogEvent(const EventReason& reason) {
UserChargingEvent proto;
proto.mutable_event()->set_event_id(++event_id_);
proto.mutable_event()->set_reason(reason);
......@@ -261,6 +405,12 @@ void SmartChargingManager::LogEvent(
user_charging_event_for_test_ = proto;
ukm_logger_->LogEvent(proto);
AddPastEvent(reason);
// Calls |UpdatePastEvents()| after |AddPastEvent()| to keep the number of
// saved past events to be minimum.
UpdatePastEvents();
MaybeSaveToDisk();
}
void SmartChargingManager::OnTimerFired() {
......@@ -306,5 +456,143 @@ base::TimeDelta SmartChargingManager::DurationRecentVideoPlaying() {
return total_time;
}
void SmartChargingManager::MaybeLoadFromDisk() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::FilePath file_path;
if (GetPathSuccess(&file_path, false /*create_new_path*/)) {
LoadFromDisk(file_path);
loaded_from_disk_ = true;
}
}
void SmartChargingManager::MaybeSaveToDisk() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::FilePath file_path;
if (GetPathSuccess(&file_path, true /*create_new_path*/))
SaveToDisk(file_path);
}
void SmartChargingManager::OnLoadProtoFromDiskComplete(
std::unique_ptr<PastChargingEvents> proto) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!proto) {
return;
}
for (const auto& event : proto.get()->events()) {
past_events_.emplace_back(event);
}
}
void SmartChargingManager::LoadFromDisk(const base::FilePath& file_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(), FROM_HERE,
base::BindOnce(&ReadProto, file_path),
base::BindOnce(&SmartChargingManager::OnLoadProtoFromDiskComplete,
weak_ptr_factory_.GetWeakPtr()));
}
void SmartChargingManager::SaveToDisk(const base::FilePath& file_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PastChargingEvents proto;
for (const auto& event : past_events_) {
*proto.add_events() = event;
}
blocking_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WriteProtoToDisk, proto, file_path));
}
void SmartChargingManager::AddPastEvent(const EventReason& reason) {
// Since we use |past_events_| to calculate last charge information, we don't
// need to save if the reason is PERIODIC_LOG.
if (reason == UserChargingEvent::Event::PERIODIC_LOG)
return;
PastEvent new_event;
const base::Time now = base::Time::Now();
new_event.set_time(now.ToDeltaSinceWindowsEpoch().InMinutes());
if (battery_percent_.has_value())
new_event.set_battery_percent(static_cast<int>(battery_percent_.value()));
new_event.set_timezone((now.UTCMidnight() - now.LocalMidnight()).InHours());
new_event.set_reason(reason);
past_events_.emplace_back(new_event);
}
void SmartChargingManager::UpdatePastEvents() {
PastEvent last_charge_plugged_in;
PastEvent last_charge_unplugged;
PastEvent new_plugged_in;
PastEvent new_halt;
std::tie(last_charge_plugged_in, last_charge_unplugged) =
GetLastChargeEvents();
if (last_charge_unplugged.has_time()) {
// Gets the unplugged and halt(shutdown/suspend) events after the unplug (if
// any).
for (const auto& event : past_events_) {
if (event.time() > last_charge_unplugged.time()) {
if (event.reason() == UserChargingEvent::Event::CHARGER_PLUGGED_IN) {
new_plugged_in = event;
} else if (IsHaltEvent(event)) {
new_halt = event;
}
}
}
} else {
// Gets the last halt and plugged in event.
for (const auto& event : past_events_) {
if (event.reason() == UserChargingEvent::Event::CHARGER_PLUGGED_IN) {
new_plugged_in = event;
}
if (IsHaltEvent(event)) {
new_halt = event;
}
}
}
// Removes everything else.
past_events_.clear();
// Adds useful events back.
if (last_charge_plugged_in.has_time())
past_events_.emplace_back(last_charge_plugged_in);
if (last_charge_unplugged.has_time())
past_events_.emplace_back(last_charge_unplugged);
if (new_plugged_in.has_time())
past_events_.emplace_back(new_plugged_in);
if (new_halt.has_time())
past_events_.emplace_back(new_halt);
}
// Returns the last pair of plug/unplug events. If can't find the last pair,
// return a pair of empty events.
std::tuple<PastEvent, PastEvent> SmartChargingManager::GetLastChargeEvents() {
PastEvent plugged_in;
PastEvent unplugged;
PastEvent temp_plugged_in;
// There could be multiple events with CHARGER_PLUGGED_IN and/or
// CHARGER_UNPLUGGED. This function relies on the fact that all events are
// sorted by time.
for (const auto& event : past_events_) {
if (event.has_reason()) {
if (event.reason() == UserChargingEvent::Event::CHARGER_PLUGGED_IN) {
temp_plugged_in = event;
} else if (event.reason() ==
UserChargingEvent::Event::CHARGER_UNPLUGGED) {
if (!temp_plugged_in.has_time())
continue;
// Updates the pair of results.
if (!plugged_in.has_time() ||
temp_plugged_in.time() != plugged_in.time()) {
plugged_in = temp_plugged_in;
unplugged = event;
}
}
}
}
return std::make_tuple(plugged_in, unplugged);
}
} // namespace power
} // namespace chromeos
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CHROMEOS_POWER_SMART_CHARGING_SMART_CHARGING_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_POWER_SMART_CHARGING_SMART_CHARGING_MANAGER_H_
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
......@@ -16,6 +17,8 @@
#include "chrome/browser/chromeos/power/smart_charging/smart_charging_ukm_logger.h"
#include "chrome/browser/chromeos/power/smart_charging/user_charging_event.pb.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/viz/public/mojom/compositing/video_detector_observer.mojom.h"
#include "ui/base/user_activity/user_activity_detector.h"
......@@ -27,16 +30,21 @@ namespace ml {
class RecentEventsCounter;
} // namespace ml
using PastEvent = PastChargingEvents::Event;
using EventReason = UserChargingEvent::Event::Reason;
// SmartChargingManager logs battery percentage and other features related to
// user charging events. It is currently used to log data and will be
// extended to do inference in the future.
class SmartChargingManager : public ui::UserActivityObserver,
public PowerManagerClient::Observer,
public viz::mojom::VideoDetectorObserver {
public viz::mojom::VideoDetectorObserver,
public session_manager::SessionManagerObserver {
public:
SmartChargingManager(
ui::UserActivityDetector* detector,
mojo::PendingReceiver<viz::mojom::VideoDetectorObserver> receiver,
session_manager::SessionManager* session_manager,
std::unique_ptr<base::RepeatingTimer> periodic_timer);
~SmartChargingManager() override;
SmartChargingManager(const SmartChargingManager&) = delete;
......@@ -73,6 +81,9 @@ class SmartChargingManager : public ui::UserActivityObserver,
void OnVideoActivityStarted() override;
void OnVideoActivityEnded() override;
// session_manager::SessionManagerObserver overrides:
void OnUserSessionStarted(bool is_primary_user) override;
private:
friend class SmartChargingManagerTest;
......@@ -80,7 +91,7 @@ class SmartChargingManager : public ui::UserActivityObserver,
void PopulateUserChargingEventProto(UserChargingEvent* proto);
// Log the event.
void LogEvent(const UserChargingEvent::Event::Reason& reason);
void LogEvent(const EventReason& reason);
// Called when the periodic timer triggers.
void OnTimerFired();
......@@ -97,16 +108,47 @@ class SmartChargingManager : public ui::UserActivityObserver,
// minutes).
base::TimeDelta DurationRecentVideoPlaying();
// Checks if the file for current user exists, if yes loads from disk.
void MaybeLoadFromDisk();
// Checks if we can create a path on disk to save the data, if yes saves to
// disk.
void MaybeSaveToDisk();
// Loads data from disk given a file path.
void LoadFromDisk(const base::FilePath& file_path);
// Saves data to disk given a file path.
void SaveToDisk(const base::FilePath& file_path);
// Calls after SaveToDisk completes.
void OnLoadProtoFromDiskComplete(std::unique_ptr<PastChargingEvents> proto);
// Adds a past events given it's reason to |past_events_|.
void AddPastEvent(const EventReason& reason);
// Updates and deletes events.
void UpdatePastEvents();
// Gets the "plug in" and "unplug" events of the last charge.
std::tuple<PastEvent, PastEvent> GetLastChargeEvents();
ScopedObserver<ui::UserActivityDetector, ui::UserActivityObserver>
user_activity_observer_{this};
ScopedObserver<chromeos::PowerManagerClient,
chromeos::PowerManagerClient::Observer>
power_manager_client_observer_{this};
ScopedObserver<session_manager::SessionManager,
session_manager::SessionManagerObserver>
session_manager_observer_{this};
// Timer to trigger periodically for logging data.
const std::unique_ptr<base::RepeatingTimer> periodic_timer_;
// Checks if data is loaded from disk yet.
bool loaded_from_disk_ = false;
// Helper to return TimeSinceBoot.
ml::BootClock boot_clock_;
int event_id_ = -1;
......@@ -134,6 +176,7 @@ class SmartChargingManager : public ui::UserActivityObserver,
// TODO(crbug.com/1028853): This is for testing only. Need to remove when ukm
// logger is available.
UserChargingEvent user_charging_event_for_test_;
std::vector<PastEvent> past_events_;
base::Optional<double> battery_percent_;
base::Optional<double> screen_brightness_percent_;
......@@ -143,6 +186,7 @@ class SmartChargingManager : public ui::UserActivityObserver,
const std::unique_ptr<SmartChargingUkmLogger> ukm_logger_;
SEQUENCE_CHECKER(sequence_checker_);
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
base::WeakPtrFactory<SmartChargingManager> weak_ptr_factory_{this};
};
......
......@@ -4,15 +4,32 @@
#include "chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/clock.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/power/smart_charging/user_charging_event.pb.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/session_manager/core/session_manager.h"
#include "ui/events/keycodes/dom/dom_code.h"
namespace chromeos {
namespace power {
namespace {
PastEvent CreateEvent(int time,
int battery_percent,
int timezone,
const EventReason& reason) {
PastEvent event;
event.set_time(time);
event.set_battery_percent(battery_percent);
event.set_timezone(timezone);
event.set_reason(reason);
return event;
}
} // namespace
class SmartChargingManagerTest : public ChromeRenderViewHostTestHarness {
public:
......@@ -36,7 +53,7 @@ class SmartChargingManagerTest : public ChromeRenderViewHostTestHarness {
task_environment()->GetMainThreadTaskRunner());
smart_charging_manager_ = std::make_unique<SmartChargingManager>(
&user_activity_detector_, observer.InitWithNewPipeAndPassReceiver(),
std::move(periodic_timer));
&session_manager_, std::move(periodic_timer));
}
void TearDown() override {
......@@ -112,6 +129,40 @@ class SmartChargingManagerTest : public ChromeRenderViewHostTestHarness {
return smart_charging_manager_->DurationRecentVideoPlaying();
}
std::tuple<PastEvent, PastEvent> GetLastChargeEvents() {
return smart_charging_manager_->GetLastChargeEvents();
}
void UpdatePastEvents() { smart_charging_manager_->UpdatePastEvents(); }
std::vector<PastEvent> GetPastEvents() {
return smart_charging_manager_->past_events_;
}
void SetBatteryPercentage(double battery_percent) {
smart_charging_manager_->battery_percent_ = battery_percent;
}
void AddEvent(const PastEvent& event) {
smart_charging_manager_->past_events_.emplace_back(event);
}
void AddPastEvent(const EventReason& reason) {
smart_charging_manager_->AddPastEvent(reason);
}
void SaveToDisk(const base::FilePath& file_path) {
smart_charging_manager_->SaveToDisk(file_path);
}
void LoadFromDisk(const base::FilePath& file_path) {
smart_charging_manager_->LoadFromDisk(file_path);
}
void ClearPastEvents() { smart_charging_manager_->past_events_.clear(); }
void Wait() { task_environment()->RunUntilIdle(); }
const gfx::Point kEventLocation = gfx::Point(90, 90);
const ui::MouseEvent kMouseEvent = ui::MouseEvent(ui::ET_MOUSE_MOVED,
kEventLocation,
......@@ -122,6 +173,7 @@ class SmartChargingManagerTest : public ChromeRenderViewHostTestHarness {
private:
ui::UserActivityDetector user_activity_detector_;
session_manager::SessionManager session_manager_;
std::unique_ptr<SmartChargingManager> smart_charging_manager_;
};
......@@ -281,5 +333,184 @@ TEST_F(SmartChargingManagerTest, DeviceMode) {
EXPECT_EQ(GetUserChargingEvent().features().device_mode(),
UserChargingEvent::Features::LAPTOP_MODE);
}
TEST_F(SmartChargingManagerTest, GetLastChargeEventsNoLastCharges) {
AddEvent(CreateEvent(1, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(2, 20, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(3, 30, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(
CreateEvent(4, 40, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(5, 50, 11, UserChargingEvent::Event::SHUTDOWN));
AddEvent(CreateEvent(6, 60, 11, UserChargingEvent::Event::PERIODIC_LOG));
PastEvent plugged_in;
PastEvent unplugged;
std::tie(plugged_in, unplugged) = GetLastChargeEvents();
EXPECT_FALSE(plugged_in.has_time());
EXPECT_FALSE(unplugged.has_time());
}
TEST_F(SmartChargingManagerTest, GetLastChargeEventsComplex) {
AddEvent(CreateEvent(1, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(2, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(3, 20, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(4, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(5, 30, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(
CreateEvent(6, 20, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(7, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(8, 30, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(9, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(10, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(11, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(12, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(13, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(14, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(
CreateEvent(15, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(16, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(17, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(18, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(19, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(20, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(21, 40, 1, UserChargingEvent::Event::SHUTDOWN));
AddEvent(
CreateEvent(22, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
PastEvent plugged_in;
PastEvent unplugged;
std::tie(plugged_in, unplugged) = GetLastChargeEvents();
EXPECT_TRUE(plugged_in.has_time());
EXPECT_TRUE(unplugged.has_time());
EXPECT_EQ(plugged_in.time(), 15);
EXPECT_EQ(unplugged.time(), 18);
}
TEST_F(SmartChargingManagerTest, UpdatePastEventsNoLastCharge) {
AddEvent(CreateEvent(1, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(2, 20, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(3, 30, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(
CreateEvent(4, 40, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(5, 50, 11, UserChargingEvent::Event::SHUTDOWN));
AddEvent(CreateEvent(6, 60, 11, UserChargingEvent::Event::PERIODIC_LOG));
UpdatePastEvents();
const std::vector<PastEvent> events = GetPastEvents();
EXPECT_EQ(events.size(), static_cast<unsigned long>(2));
EXPECT_EQ(events[0].time(), 4);
EXPECT_EQ(events[1].time(), 5);
}
TEST_F(SmartChargingManagerTest, UpdatePastEventsComplex) {
AddEvent(CreateEvent(1, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(2, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(3, 20, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(4, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(5, 30, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(
CreateEvent(6, 20, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(7, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(8, 30, 11, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(9, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(10, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(11, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(12, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(13, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(14, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(
CreateEvent(15, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(16, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(17, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(18, 20, 1, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(19, 10, 1, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(
CreateEvent(20, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
AddEvent(CreateEvent(21, 40, 1, UserChargingEvent::Event::SHUTDOWN));
AddEvent(
CreateEvent(22, 40, 1, UserChargingEvent::Event::CHARGER_PLUGGED_IN));
UpdatePastEvents();
const std::vector<PastEvent> events = GetPastEvents();
EXPECT_EQ(events.size(), static_cast<unsigned long>(4));
EXPECT_EQ(events[0].time(), 15);
EXPECT_EQ(events[1].time(), 18);
EXPECT_EQ(events[2].time(), 22);
EXPECT_EQ(events[3].time(), 21);
}
TEST_F(SmartChargingManagerTest, AddPastEventTest) {
SetBatteryPercentage(15.5);
AddPastEvent(UserChargingEvent::Event::CHARGER_PLUGGED_IN);
SetBatteryPercentage(25.7);
AddPastEvent(UserChargingEvent::Event::CHARGER_UNPLUGGED);
const std::vector<PastEvent> events = GetPastEvents();
EXPECT_EQ(events.size(), static_cast<unsigned long>(2));
EXPECT_EQ(events[0].battery_percent(), 15);
EXPECT_EQ(events[0].reason(), UserChargingEvent::Event::CHARGER_PLUGGED_IN);
EXPECT_EQ(events[1].battery_percent(), 25);
EXPECT_EQ(events[1].reason(), UserChargingEvent::Event::CHARGER_UNPLUGGED);
}
TEST_F(SmartChargingManagerTest, LoadAndSave) {
AddEvent(CreateEvent(1, 10, 11, UserChargingEvent::Event::PERIODIC_LOG));
AddEvent(CreateEvent(2, 20, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
AddEvent(CreateEvent(3, 30, 11, UserChargingEvent::Event::CHARGER_UNPLUGGED));
EXPECT_EQ(GetPastEvents().size(), static_cast<unsigned long>(3));
// Save to disk
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath file_path =
temp_dir.GetPath().AppendASCII("smartcharging.txt");
SaveToDisk(file_path);
Wait();
ASSERT_TRUE(base::PathExists(file_path));
// Clear memory
ClearPastEvents();
// Now there is no past event on memory
EXPECT_EQ(GetPastEvents().size(), static_cast<unsigned long>(0));
// Load from disk
LoadFromDisk(file_path);
Wait();
// Check the result
const std::vector<PastEvent> events = GetPastEvents();
EXPECT_EQ(events.size(), static_cast<unsigned long>(3));
EXPECT_EQ(events[0].time(), 1);
EXPECT_EQ(events[1].time(), 2);
EXPECT_EQ(events[2].time(), 3);
}
TEST_F(SmartChargingManagerTest, LastChargeRelatedFeatures) {
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::AC, 23.0f);
FastForwardTimeBySecs(3600);
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::DISCONNECTED,
80.0f);
FastForwardTimeBySecs(600);
ReportShutdownEvent();
FastForwardTimeBySecs(1800);
ReportPowerChangeEvent(power_manager::PowerSupplyProperties::AC, 75.0f);
const auto features = GetUserChargingEvent().features();
EXPECT_TRUE(features.halt_from_last_charge());
EXPECT_EQ(features.time_since_last_charge(), 38);
EXPECT_EQ(features.duration_of_last_charge(), 58);
EXPECT_EQ(features.battery_percentage_before_last_charge(), 23);
EXPECT_EQ(features.battery_percentage_of_last_charge(), 80);
}
} // namespace power
} // namespace chromeos
......@@ -117,3 +117,21 @@ message UserChargingEvent {
optional Features features = 1;
optional Event event = 2;
}
// PastChargingEvents contain a list of events that have information about "past
// charging events". It will only store the plug/unplug pair of the last charge
// and a recent plug/halt event if any.
message PastChargingEvents {
message Event {
// Time of the event in minutes since Windows epoch.
optional int32 time = 1;
// Battery percentage of the device.
optional int32 battery_percent = 2;
// Timezone of the device.
optional int32 timezone = 3;
// Reason for the event.
optional UserChargingEvent.Event.Reason reason = 4;
}
// A list containing past charging events.
repeated Event events = 1;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment