Commit 97b4d4c0 authored by David Munro's avatar David Munro Committed by Chromium LUCI CQ

crostini: Engagement metrics

We track time with a GUI window in foreground and time with Crostini
running in the background. We do not yet track time with a terminal
browser window in foreground.
Note that the actual metric-emitting code is at
https://osscs.corp.google.com/chromium/chromium/src/+/master:components/guest_os/guest_os_engagement_metrics.cc;l=177;drc=4cc56e90bebaf1df23e738c19b88463cb4e2f366

Bug: chromium:1007139
Test: Deploy, use Crostini, check chrome://histograms
Change-Id: Ib7b0d6d88d7c0a4d94fcb3c399c41a243ccccbe1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2586649
Commit-Queue: David Munro <davidmunro@google.com>
Reviewed-by: default avatarNicholas Verne <nverne@chromium.org>
Reviewed-by: default avatarAlexander Alekseev <alemate@chromium.org>
Reviewed-by: default avatarCaitlin Fischer <caitlinfischer@google.com>
Reviewed-by: default avatarTimothy Loh <timloh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#838580}
parent 854d21e5
......@@ -1086,6 +1086,8 @@ source_set("chromeos") {
"crostini/ansible/ansible_management_service_factory.h",
"crostini/crostini_disk.cc",
"crostini/crostini_disk.h",
"crostini/crostini_engagement_metrics_service.cc",
"crostini/crostini_engagement_metrics_service.h",
"crostini/crostini_export_import.cc",
"crostini/crostini_export_import.h",
"crostini/crostini_export_import_notification_controller.cc",
......
......@@ -10,6 +10,7 @@
#include "chrome/browser/chromeos/authpolicy/authpolicy_credentials_manager.h"
#include "chrome/browser/chromeos/bluetooth/debug_logs_manager_factory.h"
#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h"
#include "chrome/browser/chromeos/crostini/crostini_engagement_metrics_service.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/chromeos/extensions/input_method_api.h"
#include "chrome/browser/chromeos/extensions/login_screen/login_state/session_state_changed_event_dispatcher.h"
......@@ -61,6 +62,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
cert_provisioning::CertProvisioningSchedulerUserServiceFactory::GetInstance();
chromeos::full_restore::FullRestoreServiceFactory::GetInstance();
CroshLoaderFactory::GetInstance();
crostini::CrostiniEngagementMetricsService::Factory::GetInstance();
#if defined(USE_CUPS)
CupsProxyServiceManagerFactory::GetInstance();
#endif
......
// Copyright 2020 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.
#include "chrome/browser/chromeos/crostini/crostini_engagement_metrics_service.h"
#include "chrome/browser/chromeos/crostini/crostini_features.h"
#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
namespace crostini {
constexpr char kUmaPrefix[] = "Crostini";
CrostiniEngagementMetricsService*
CrostiniEngagementMetricsService::Factory::GetForProfile(Profile* profile) {
return static_cast<CrostiniEngagementMetricsService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
CrostiniEngagementMetricsService::Factory*
CrostiniEngagementMetricsService::Factory::GetInstance() {
static base::NoDestructor<CrostiniEngagementMetricsService::Factory> factory;
return factory.get();
}
CrostiniEngagementMetricsService::Factory::Factory()
: BrowserContextKeyedServiceFactory(
"CrostiniEngagementMetricsService",
BrowserContextDependencyManager::GetInstance()) {}
CrostiniEngagementMetricsService::Factory::~Factory() = default;
KeyedService*
CrostiniEngagementMetricsService::Factory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
return new CrostiniEngagementMetricsService(profile);
}
bool CrostiniEngagementMetricsService::Factory::
ServiceIsCreatedWithBrowserContext() const {
return true;
}
bool CrostiniEngagementMetricsService::Factory::ServiceIsNULLWhileTesting()
const {
// Checking whether Crostini is allowed requires more setup than is present
// in most unit tests.
return true;
}
CrostiniEngagementMetricsService::CrostiniEngagementMetricsService(
Profile* profile) {
if (!CrostiniFeatures::Get()->IsEnabled(profile))
return;
guest_os_engagement_metrics_ =
std::make_unique<guest_os::GuestOsEngagementMetrics>(
profile->GetPrefs(), base::BindRepeating(IsCrostiniWindow),
prefs::kEngagementPrefsPrefix, kUmaPrefix);
}
CrostiniEngagementMetricsService::~CrostiniEngagementMetricsService() = default;
void CrostiniEngagementMetricsService::SetBackgroundActive(
bool background_active) {
// If policy changes to enable Crostini, we won't have created the helper
// object. This should be relatively rare so for now we don't track this
// case.
if (!guest_os_engagement_metrics_)
return;
guest_os_engagement_metrics_->SetBackgroundActive(background_active);
}
} // namespace crostini
// Copyright 2020 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_CROSTINI_CROSTINI_ENGAGEMENT_METRICS_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_ENGAGEMENT_METRICS_SERVICE_H_
#include "base/macros.h"
#include "base/no_destructor.h"
#include "components/guest_os/guest_os_engagement_metrics.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
class Profile;
namespace crostini {
// A KeyedService for tracking engagement with Crostini, reporting values as
// per GuestOsEngagementMetrics.
class CrostiniEngagementMetricsService : public KeyedService {
public:
class Factory : public BrowserContextKeyedServiceFactory {
public:
static CrostiniEngagementMetricsService* GetForProfile(Profile* profile);
static Factory* GetInstance();
private:
friend class base::NoDestructor<Factory>;
Factory();
~Factory() override;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
bool ServiceIsNULLWhileTesting() const override;
};
explicit CrostiniEngagementMetricsService(Profile* profile);
~CrostiniEngagementMetricsService() override;
// This needs to be called when Crostini starts and stops being active so we
// can correctly track background active time.
void SetBackgroundActive(bool background_active);
private:
std::unique_ptr<guest_os::GuestOsEngagementMetrics>
guest_os_engagement_metrics_;
DISALLOW_COPY_AND_ASSIGN(CrostiniEngagementMetricsService);
};
} // namespace crostini
#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_ENGAGEMENT_METRICS_SERVICE_H_
......@@ -29,6 +29,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/crostini/ansible/ansible_management_service.h"
#include "chrome/browser/chromeos/crostini/crostini_engagement_metrics_service.h"
#include "chrome/browser/chromeos/crostini/crostini_features.h"
#include "chrome/browser/chromeos/crostini/crostini_manager_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_port_forwarder.h"
......@@ -2569,6 +2570,13 @@ void CrostiniManager::OnContainerStarted(
return;
ContainerId container_id(signal.vm_name(), signal.container_name());
auto* engagement_metrics_service =
CrostiniEngagementMetricsService::Factory::GetForProfile(profile_);
// This is null in unit tests.
if (engagement_metrics_service) {
engagement_metrics_service->SetBackgroundActive(true);
}
running_containers_.emplace(
signal.vm_name(),
ContainerInfo(signal.container_name(), signal.container_username(),
......@@ -2667,6 +2675,14 @@ void CrostiniManager::OnContainerShutdown(
break;
}
}
if (running_containers_.empty()) {
auto* engagement_metrics_service =
CrostiniEngagementMetricsService::Factory::GetForProfile(profile_);
// This is null in unit tests.
if (engagement_metrics_service) {
engagement_metrics_service->SetBackgroundActive(false);
}
}
}
void CrostiniManager::OnInstallLinuxPackageProgress(
......
......@@ -10,6 +10,7 @@
#include "base/values.h"
#include "chrome/browser/chromeos/crostini/crostini_simple_types.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "components/guest_os/guest_os_prefs.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
......@@ -85,6 +86,9 @@ const char kCrostiniPortForwarding[] = "crostini.port_forwarding.ports";
const char kCrostiniArcAdbSideloadingUserPref[] =
"crostini.arc_adb_sideloading.user_pref";
// Prefix for storing engagement time data, per GuestOSEngagementMetrics.
const char kEngagementPrefsPrefix[] = "crostini.metrics";
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kCrostiniEnabled, false);
registry->RegisterDictionaryPref(kCrostiniMimeTypes);
......@@ -129,6 +133,9 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(
kCrostiniArcAdbSideloadingUserPref,
static_cast<int>(CrostiniArcAdbSideloadingUserAllowanceMode::kDisallow));
guest_os::prefs::RegisterEngagementProfilePrefs(registry,
kEngagementPrefsPrefix);
}
} // namespace prefs
......
......@@ -42,6 +42,8 @@ extern const char kCrostiniLastLaunchTimeWindowStart[];
extern const char kCrostiniLastDiskSize[];
extern const char kCrostiniPortForwarding[];
extern const char kEngagementPrefsPrefix[];
void RegisterProfilePrefs(PrefRegistrySimple* registry);
} // namespace prefs
......
......@@ -572,7 +572,12 @@ const ContainerId& DefaultContainerId() {
return *container_id;
}
bool IsCrostiniWindow(aura::Window* window) {
bool IsCrostiniWindow(const aura::Window* window) {
// TODO(crbug/1158644): Non-Crostini apps (borealis, ...) have also been
// identifying as Crostini. For now they're less common, and as they become
// more productionised they get their own app type (e.g. lacros), but at some
// point we'll want to untangle these different types to e.g. avoid double
// counting in usage metrics.
return window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::CROSTINI_APP);
}
......
......@@ -209,7 +209,7 @@ void UpdateContainerPref(Profile* profile,
const ContainerId& DefaultContainerId();
bool IsCrostiniWindow(aura::Window* window);
bool IsCrostiniWindow(const aura::Window* window);
} // namespace crostini
......
......@@ -241,6 +241,38 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary>
</histogram>
<histogram name="Crostini.EngagementTime.{Variant}" units="ms"
expires_after="2021-04-18">
<owner>davidmunro@google.com</owner>
<owner>clumptini@google.com</owner>
<summary>
{Variant} Engagement time metrics, along with foreground and background
time, are only collected on users with Crostini enabled. All four metrics
are accumulated and recorded to UMA once a day.
</summary>
<token key="Variant">
<variant name="Background"
summary="Times when a user is engaged and Crostini apps are running
in the background, but the user isn't focusing on an
Crostini app window."/>
<variant name="CrostiniTotal"
summary="Total of Crostini.EngagementTime.Background and .Foreground,
which is the time the user is engaged and Crostini is
running either in the foreground or background."/>
<variant name="Foreground"
summary="Times when a user is engaged and focuses on a Crostini GUI
window. As of 2020-12-15 this may count some
similar-but-not-quite-Crostini windows e.g. Bruschetta, see
crbug/1158644 for more details."/>
<variant name="Total"
summary="Total CrOS user session time (from login to logout)
excluding times when a user &quot;disengages&quot;. A user
is disengaged when the screen is locked or dimmed due to
user inactivity. For Total Crostini engagement time, see
Crostini.EngagementTime.CrostiniTotal."/>
</token>
</histogram>
<histogram name="Crostini.FilesystemCorruption" enum="CorruptionStates"
expires_after="2021-06-13">
<owner>clumptini@google.com</owner>
......
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