Commit 4f75e719 authored by Rachel Wong's avatar Rachel Wong Committed by Commit Bot

[settings logging] Log shared features.

This change implements logging of features that are shared by all quick
settings logging functions.

- Audio feature was previously only logged for a volume change,  but has
been moved to become a shared feature.
- There is also some reordering of the header file for readability
reasons.

Bug: 1014839
Change-Id: I4fc6cc9dc4d76acae5450894ec49258320b02d24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2087300Reviewed-by: default avatarJia Meng <jiameng@chromium.org>
Commit-Queue: Rachel Wong <wrong@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748073}
parent 073c54bd
......@@ -1493,6 +1493,7 @@ component("ash") {
"//chromeos/strings",
"//chromeos/system",
"//components/account_id",
"//components/country_codes",
"//components/device_event_log",
"//components/discardable_memory/service",
"//components/exo",
......
......@@ -5,11 +5,16 @@
#include "ash/system/machine_learning/user_settings_event_logger.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/app_list/app_list_client.h"
#include "ash/shell.h"
#include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/power/power_status.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
......@@ -47,7 +52,8 @@ UserSettingsEventLogger::UserSettingsEventLogger()
is_recently_presenting_(false),
is_recently_fullscreen_(false),
used_cellular_in_session_(false),
is_playing_audio_(false) {
is_playing_audio_(false),
clock_(base::DefaultClock::GetInstance()) {
Shell::Get()->AddShellObserver(this);
chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
}
......@@ -190,8 +196,6 @@ void UserSettingsEventLogger::OnVolumeTimerEnded() {
event->set_previous_value(previous_volume_);
event->set_current_value(current_volume_);
settings_event.mutable_features()->set_is_playing_audio(is_playing_audio_);
PopulateSharedFeatures(&settings_event);
SendToUkmAndAppList(settings_event);
}
......@@ -270,8 +274,46 @@ void UserSettingsEventLogger::OnOutputStopped() {
is_playing_audio_ = false;
}
void UserSettingsEventLogger::PopulateSharedFeatures(UserSettingsEvent* event) {
// TODO(crbug/1014839): Populate the shared contextual features.
void UserSettingsEventLogger::SetClockForTesting(const base::Clock* clock) {
clock_ = clock;
}
void UserSettingsEventLogger::PopulateSharedFeatures(
UserSettingsEvent* settings_event) {
auto* features = settings_event->mutable_features();
// Set time features.
base::Time::Exploded now;
clock_->Now().LocalExplode(&now);
features->set_hour_of_day(now.hour);
features->set_day_of_week(
static_cast<UserSettingsEvent::Features::DayOfWeek>(now.day_of_week));
// Set power features.
if (PowerStatus::IsInitialized()) {
const auto* power_status = PowerStatus::Get();
features->set_battery_percentage(power_status->GetRoundedBatteryPercent());
features->set_is_charging(power_status->IsLinePowerConnected() ||
power_status->IsMainsChargerConnected() ||
power_status->IsUsbChargerConnected());
}
// Set activity features.
features->set_is_playing_audio(is_playing_audio_);
// TODO(crbug/1014839): Set the |is_playing_video| field.
// Set orientation features.
features->set_device_mode(
Shell::Get()->tablet_mode_controller()->InTabletMode()
? UserSettingsEvent::Features::TABLET_MODE
: UserSettingsEvent::Features::CLAMSHELL_MODE);
const auto orientation =
Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
if (IsLandscapeOrientation(orientation)) {
features->set_device_orientation(UserSettingsEvent::Features::LANDSCAPE);
} else if (IsPortraitOrientation(orientation)) {
features->set_device_orientation(UserSettingsEvent::Features::PORTRAIT);
}
}
void UserSettingsEventLogger::SendToUkmAndAppList(
......@@ -295,8 +337,21 @@ void UserSettingsEventLogger::SendToUkmAndAppList(
if (event.has_accessibility_id())
ukm_event.SetAccessibilityId(event.accessibility_id());
if (features.has_hour_of_day())
ukm_event.SetHourOfDay(features.hour_of_day());
if (features.has_day_of_week())
ukm_event.SetDayOfWeek(features.day_of_week());
if (features.has_battery_percentage())
ukm_event.SetBatteryPercentage(features.battery_percentage());
if (features.has_is_charging())
ukm_event.SetIsCharging(features.is_charging());
if (features.has_is_playing_audio())
ukm_event.SetIsPlayingAudio(features.is_playing_audio());
if (features.has_device_mode())
ukm_event.SetDeviceMode(features.device_mode());
if (features.has_device_orientation())
ukm_event.SetDeviceOrientation(features.device_orientation());
if (features.has_is_recently_presenting())
ukm_event.SetIsRecentlyPresenting(features.is_recently_presenting());
if (features.has_is_recently_fullscreen())
......
......@@ -10,6 +10,7 @@
#include "ash/system/bluetooth/tray_bluetooth_helper.h"
#include "ash/system/machine_learning/user_settings_event.pb.h"
#include "base/sequence_checker.h"
#include "base/time/clock.h"
#include "base/timer/timer.h"
#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h"
......@@ -72,44 +73,49 @@ class ASH_EXPORT UserSettingsEventLogger
void OnOutputStarted() override;
void OnOutputStopped() override;
void SetClockForTesting(const base::Clock* clock);
private:
friend class UserSettingsEventLoggerTest;
UserSettingsEventLogger();
~UserSettingsEventLogger() override;
// Populates contextual information shared by all settings events.
void PopulateSharedFeatures(UserSettingsEvent* event);
void PopulateSharedFeatures(UserSettingsEvent* settings_event);
// Sends the given event to UKM and AppListClient.
void SendToUkmAndAppList(const UserSettingsEvent& event);
void SendToUkmAndAppList(const UserSettingsEvent& settings_event);
void OnVolumeTimerEnded();
void OnBrightnessTimerEnded();
void OnPresentingTimerEnded();
void OnFullscreenTimerEnded();
// When the user drags the brightness or volume slider, the logger will be
// called multiple times at small time increments. These timers are used so
// that a UKM event is only logged after there has been a pause.
// Timer to ensure that volume is only recorded after a pause.
base::OneShotTimer volume_timer_;
base::OneShotTimer brightness_timer_;
// Levels before the corresponding timer was started.
int previous_volume_;
int previous_brightness_;
// Levels as of the most recent event.
int current_volume_;
// Timer to ensure that brightness is only recorded after a pause.
base::OneShotTimer brightness_timer_;
int previous_brightness_;
int current_brightness_;
base::OneShotTimer presenting_timer_;
base::OneShotTimer fullscreen_timer_;
int presenting_session_count_;
// Whether the device has been presenting in the last 5 minutes.
bool is_recently_presenting_;
base::OneShotTimer fullscreen_timer_;
// Whether the device has been in fullscreen mode in the last 5 minutes.
bool is_recently_fullscreen_;
bool used_cellular_in_session_;
bool is_playing_audio_;
const base::Clock* clock_;
SEQUENCE_CHECKER(sequence_checker_);
static UserSettingsEventLogger* instance_;
......
......@@ -4,14 +4,22 @@
#include "ash/system/machine_learning/user_settings_event_logger.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/shell.h"
#include "ash/system/machine_learning/user_settings_event.pb.h"
#include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/power/power_status.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
namespace ash {
namespace ml {
......@@ -26,6 +34,8 @@ using chromeos::network_config::mojom::NetworkTypeStateProperties;
using chromeos::network_config::mojom::SecurityType;
using chromeos::network_config::mojom::WiFiStateProperties;
using chromeos::network_config::mojom::WiFiStatePropertiesPtr;
using display::Display;
using power_manager::PowerSupplyProperties;
using ukm::TestUkmRecorder;
NetworkStatePropertiesPtr CreateWifiNetwork(int signal_strength,
......@@ -121,6 +131,12 @@ class UserSettingsEventLoggerTest : public AshTestBase {
task_environment_->FastForwardBy(kSliderDelay);
}
void LogSharedFeatures() {
UserSettingsEvent event;
logger_->PopulateSharedFeatures(&event);
logger_->SendToUkmAndAppList(event);
}
UserSettingsEventLogger* logger_;
private:
......@@ -313,15 +329,10 @@ TEST_F(UserSettingsEventLoggerTest, TestLogAccessibilityEvent) {
TEST_F(UserSettingsEventLoggerTest, TestLogVolumeFeatures) {
LogVolumeAndWait(23, 98);
logger_->OnOutputStarted();
LogVolumeAndWait(0, 0);
logger_->OnOutputStopped();
LogVolumeAndWait(0, 0);
const auto& entries = GetUkmEntries();
ASSERT_EQ(3ul, entries.size());
ASSERT_EQ(1ul, entries.size());
// Check that the first entry has all details recorded correctly.
const auto* entry = entries[0];
TestUkmRecorder::ExpectEntryMetric(entry, "SettingId",
UserSettingsEvent::Event::VOLUME);
......@@ -330,10 +341,6 @@ TEST_F(UserSettingsEventLoggerTest, TestLogVolumeFeatures) {
TestUkmRecorder::ExpectEntryMetric(entry, "PreviousValue", 23);
TestUkmRecorder::ExpectEntryMetric(entry, "CurrentValue", 98);
TestUkmRecorder::ExpectEntryMetric(entry, "IsPlayingAudio", false);
// Check that subsequent entries correctly record |is_playing_audio|.
TestUkmRecorder::ExpectEntryMetric(entries[1], "IsPlayingAudio", true);
TestUkmRecorder::ExpectEntryMetric(entries[2], "IsPlayingAudio", false);
}
TEST_F(UserSettingsEventLoggerTest, TestVolumeDelay) {
......@@ -359,7 +366,7 @@ TEST_F(UserSettingsEventLoggerTest, TestVolumeDelay) {
TestUkmRecorder::ExpectEntryMetric(entry, "CurrentValue", 15);
}
TEST_F(UserSettingsEventLoggerTest, TestBrightnessFeatures) {
TEST_F(UserSettingsEventLoggerTest, TestLogBrightnessFeatures) {
LogBrightnessAndWait(12, 29);
// Enter fullscreen.
......@@ -416,5 +423,128 @@ TEST_F(UserSettingsEventLoggerTest, TestBrightnessDelay) {
TestUkmRecorder::ExpectEntryMetric(entry, "CurrentValue", 15);
}
TEST_F(UserSettingsEventLoggerTest, TestLogDatetimeFeatures) {
base::SimpleTestClock test_clock;
logger_->SetClockForTesting(&test_clock);
base::Time fake_time;
ASSERT_TRUE(base::Time::FromString("Thu, 7 Nov 2019 14:58:00", &fake_time));
test_clock.SetNow(fake_time);
LogSharedFeatures();
ASSERT_TRUE(base::Time::FromString("Fri, 8 Nov 2019 04:24:00", &fake_time));
test_clock.SetNow(fake_time);
LogSharedFeatures();
const auto& entries = GetUkmEntries();
ASSERT_EQ(2ul, entries.size());
const auto* entry = entries[0];
TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 14);
TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek",
UserSettingsEvent::Features::THU);
entry = entries[1];
TestUkmRecorder::ExpectEntryMetric(entry, "HourOfDay", 4);
TestUkmRecorder::ExpectEntryMetric(entry, "DayOfWeek",
UserSettingsEvent::Features::FRI);
}
TEST_F(UserSettingsEventLoggerTest, TestLogPowerFeatures) {
PowerSupplyProperties fake_power;
fake_power.set_battery_percent(56.0);
fake_power.set_external_power(PowerSupplyProperties::DISCONNECTED);
PowerStatus::Get()->SetProtoForTesting(fake_power);
LogSharedFeatures();
fake_power.set_external_power(PowerSupplyProperties::AC);
PowerStatus::Get()->SetProtoForTesting(fake_power);
LogSharedFeatures();
fake_power.set_external_power(PowerSupplyProperties::USB);
PowerStatus::Get()->SetProtoForTesting(fake_power);
LogSharedFeatures();
const auto& entries = GetUkmEntries();
ASSERT_EQ(3ul, entries.size());
auto* entry = entries[0];
TestUkmRecorder::ExpectEntryMetric(entry, "BatteryPercentage", 56);
TestUkmRecorder::ExpectEntryMetric(entry, "IsCharging", false);
TestUkmRecorder::ExpectEntryMetric(entries[1], "IsCharging", true);
TestUkmRecorder::ExpectEntryMetric(entries[2], "IsCharging", true);
}
TEST_F(UserSettingsEventLoggerTest, TestLogAudio) {
LogSharedFeatures();
logger_->OnOutputStarted();
LogSharedFeatures();
logger_->OnOutputStopped();
LogSharedFeatures();
const auto& entries = GetUkmEntries();
ASSERT_EQ(3ul, entries.size());
TestUkmRecorder::ExpectEntryMetric(entries[0], "IsPlayingAudio", false);
TestUkmRecorder::ExpectEntryMetric(entries[1], "IsPlayingAudio", true);
TestUkmRecorder::ExpectEntryMetric(entries[2], "IsPlayingAudio", false);
}
TEST_F(UserSettingsEventLoggerTest, TestLogTabletMode) {
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
tablet_mode_controller->SetEnabledForTest(true);
LogSharedFeatures();
tablet_mode_controller->SetEnabledForTest(false);
LogSharedFeatures();
const auto& entries = GetUkmEntries();
ASSERT_EQ(2ul, entries.size());
TestUkmRecorder::ExpectEntryMetric(entries[0], "DeviceMode",
UserSettingsEvent::Features::TABLET_MODE);
TestUkmRecorder::ExpectEntryMetric(
entries[1], "DeviceMode", UserSettingsEvent::Features::CLAMSHELL_MODE);
}
TEST_F(UserSettingsEventLoggerTest, TestLogOrientation) {
display::test::ScopedSetInternalDisplayId set_internal_display(
Shell::Get()->display_manager(),
display::Screen::GetScreen()->GetPrimaryDisplay().id());
ScreenOrientationControllerTestApi orientation_test_api(
Shell::Get()->screen_orientation_controller());
orientation_test_api.SetDisplayRotation(Display::ROTATE_0,
Display::RotationSource::ACTIVE);
LogSharedFeatures();
orientation_test_api.SetDisplayRotation(Display::ROTATE_90,
Display::RotationSource::ACTIVE);
LogSharedFeatures();
orientation_test_api.SetDisplayRotation(Display::ROTATE_180,
Display::RotationSource::ACTIVE);
LogSharedFeatures();
orientation_test_api.SetDisplayRotation(Display::ROTATE_270,
Display::RotationSource::ACTIVE);
LogSharedFeatures();
const auto& entries = GetUkmEntries();
ASSERT_EQ(4ul, entries.size());
TestUkmRecorder::ExpectEntryMetric(entries[0], "DeviceOrientation",
UserSettingsEvent::Features::LANDSCAPE);
TestUkmRecorder::ExpectEntryMetric(entries[1], "DeviceOrientation",
UserSettingsEvent::Features::PORTRAIT);
TestUkmRecorder::ExpectEntryMetric(entries[2], "DeviceOrientation",
UserSettingsEvent::Features::LANDSCAPE);
TestUkmRecorder::ExpectEntryMetric(entries[3], "DeviceOrientation",
UserSettingsEvent::Features::PORTRAIT);
}
} // namespace ml
} // namespace ash
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