// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_METRICS_H_
#define CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_METRICS_H_
#include <map>
#include <set>
#include <string>
#include <string_view>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
#include "chrome/browser/apps/app_service/metrics/browser_to_tab_list.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/instance_registry.h"
#include "components/services/app_service/public/protos/app_types.pb.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
class Profile;
namespace apps {
class AppUpdate;
// This is used for logging, so do not remove or reorder existing entries. Also
// needs to be kept in sync with the ApplicationInstallTime in
// //components/services/app_service/public/protos/app_types.proto.
enum class InstallTime {
kInit = 0,
kRunning = 1,
// Add any new values above this one, and update kMaxValue to the highest
// enumerator value.
kMaxValue = kRunning,
};
struct CrostiniAppId {
std::string desktop_id;
std::string registration_name;
};
extern const char kAppRunningDuration[];
extern const char kAppActivatedCount[];
extern const char kAppUsageTime[];
extern const char kAppLaunchPerAppTypeHistogramName[];
extern const char kAppLaunchPerAppTypeV2HistogramName[];
extern const char kChromeAppTabHistogramName[];
extern const char kChromeAppWindowHistogramName[];
extern const char kWebAppTabHistogramName[];
extern const char kWebAppWindowHistogramName[];
extern const char kUsageTimeAppIdKey[];
extern const char kUsageTimeAppPublisherIdKey[];
extern const char kUsageTimeAppTypeKey[];
extern const char kUsageTimeDurationKey[];
extern const char kReportingUsageTimeDurationKey[];
std::string GetAppTypeHistogramNameV2(apps::AppTypeNameV2 app_type_name);
ApplicationInstallTime ConvertInstallTimeToProtoApplicationInstallTime(
InstallTime install_time);
// Records metrics when launching apps.
void RecordAppLaunchMetrics(Profile* profile,
AppType app_type,
const std::string& app_id,
apps::LaunchSource launch_source,
apps::LaunchContainer container);
class AppPlatformMetrics : public apps::AppRegistryCache::Observer,
public apps::InstanceRegistry::Observer,
public ukm::UkmRecorder::Observer {
public:
// Observer that is notified on certain app related events like install,
// launch, uninstall, etc.
class Observer : public base::CheckedObserver {
public:
Observer() = default;
Observer(const Observer&) = delete;
Observer& operator=(const Observer&) = delete;
~Observer() override = default;
// Invoked when app install metrics are being reported.
virtual void OnAppInstalled(const std::string& app_id,
AppType app_type,
InstallSource app_install_source,
InstallReason app_install_reason,
InstallTime app_install_time) {}
// Invoked when app launch metrics are being reported.
virtual void OnAppLaunched(const std::string& app_id,
AppType app_type,
apps::LaunchSource launch_source) {}
// Invoked when app uninstall metrics are being reported.
virtual void OnAppUninstalled(const std::string& app_id,
AppType app_type,
UninstallSource app_uninstall_source) {}
// Invoked when app usage metrics are being recorded (every 5 mins). Since
// apps can have multiple instances, we also include the instance id here.
virtual void OnAppUsage(const std::string& app_id,
AppType app_type,
const base::UnguessableToken& instance_id,
base::TimeDelta running_time) {}
// Invoked when the `AppPlatformMetrics` component (being observed) is being
// destroyed.
virtual void OnAppPlatformMetricsDestroyed() {}
};
// Usage time representation for the data that is persisted in the pref store.
// Includes helpers for serialization/deserialization.
struct UsageTime {
UsageTime();
explicit UsageTime(const base::Value& value);
UsageTime(const UsageTime&) = delete;
UsageTime& operator=(const UsageTime&) = delete;
~UsageTime();
base::TimeDelta running_time;
ukm::SourceId source_id = ukm::kInvalidSourceId;
std::string app_id;
// App publisher id tracked for commercial insights reporting. This
// facilitates external components to report the publisher id that includes
// the package name for android apps, web app url for web apps, etc. which
// are public app identifiers. We use an empty string if there is no
// publisher id associated with the app.
std::string app_publisher_id;
AppTypeName app_type_name = AppTypeName::kUnknown;
bool window_is_closed = false;
// Usage time tracked for Chrome OS commercial insights reporting. Because
// we have two independent attributes that track usage time now, the pref
// store data retention period will depend on both of these attributes being
// reset, ideally after the corresponding snapshot has been reported.
base::TimeDelta reporting_usage_time;
// Converts the struct UsageTime to base::Value::Dict, e.g.:
// {
// "app_id": "hhsosodfjlsjdflkjsdlfksdf",
// "app_type": "SystemWebApp",
// "time": 3600,
// "reporting_usage_time": 1800,
// }
base::Value::Dict ConvertToDict() const;
};
explicit AppPlatformMetrics(Profile* profile,
apps::AppRegistryCache& app_registry_cache,
InstanceRegistry& instance_registry);
AppPlatformMetrics(const AppPlatformMetrics&) = delete;
AppPlatformMetrics& operator=(const AppPlatformMetrics&) = delete;
~AppPlatformMetrics() override;
// Returns the SourceId of UKM for `app_id`.
static ukm::SourceId GetSourceId(Profile* profile, const std::string& app_id);
// Returns the URL used to create the SourceId for UKM. The URL will be empty
// if nothing should be recorded for |app_id|.
//
// This is used to retrieve an app identifier that is used in UKM where UKM is
// not the logger.
static GURL GetURLForApp(Profile* profile, const std::string& app_id);
// Returns a publisher id fetched from |profile| for a given |app_id|.
static std::string GetPublisherId(Profile* profile,
const std::string& app_id);
// Returns the URL for a Borealis app_id.
static GURL GetURLForBorealis(Profile* profile, const std::string& app_id);
// Returns a crostini id struct for an app_id.
static CrostiniAppId GetIdForCrostini(Profile* profile,
const std::string& app_id);
// Informs UKM service that the source_id is no longer needed and can be
// deleted later.
static void RemoveSourceId(ukm::SourceId source_id);
// UMA metrics name for installed apps count in Chrome OS.
static std::string GetAppsCountHistogramNameForTest(
AppTypeName app_type_name);
// UMA metrics name for installed apps count per InstallReason in Chrome OS.
static std::string GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName app_type_name,
apps::InstallReason install_reason);
// UMA metrics name for apps running duration in Chrome OS.
static std::string GetAppsRunningDurationHistogramNameForTest(
AppTypeName app_type_name);
// UMA metrics name for apps running percentage in Chrome OS.
static std::string GetAppsRunningPercentageHistogramNameForTest(
AppTypeName app_type_name);
// UMA metrics name for app window activated count in Chrome OS.
static std::string GetAppsActivatedCountHistogramNameForTest(
AppTypeName app_type_name);
// UMA metrics name for apps usage time in Chrome OS for AppTypeName.
static std::string GetAppsUsageTimeHistogramNameForTest(
AppTypeName app_type_name);
// UMA metrics name for apps usage time in Chrome OS for AppTypeNameV2.
static std::string GetAppsUsageTimeHistogramNameForTest(
AppTypeNameV2 app_type_name);
void OnNewDay();
void OnTenMinutes();
void OnFiveMinutes();
// Records the app usage time AppKM each 2 hours.
void OnTwoHours();
// Records UKM when launching an app.
void RecordAppLaunchUkm(AppType app_type,
const std::string& app_id,
apps::LaunchSource launch_source,
apps::LaunchContainer container);
// Records UKM when uninstalling an app.
void RecordAppUninstallUkm(AppType app_type,
const std::string& app_id,
UninstallSource uninstall_source);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private:
struct RunningStartTime {
base::TimeTicks start_time;
AppTypeName app_type_name;
AppTypeNameV2 app_type_name_v2;
std::string app_id;
};
// AppRegistryCache::Observer:
void OnAppTypeInitialized(AppType app_type) override;
void OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) override;
void OnAppUpdate(const apps::AppUpdate& update) override;
// apps::InstanceRegistry::Observer:
void OnInstanceUpdate(const apps::InstanceUpdate& update) override;
void OnInstanceRegistryWillBeDestroyed(
apps::InstanceRegistry* cache) override;
// ukm::UkmRecorder::Observer:
// Called only in Managed Guest Session since the observation is started only
// in Managed Guest Session.
void OnStartingShutdown() override;
// Returns the browser instance app id, instance id and state for
// `browser_window`. If there is no browser instance, the returned token of
// the browser id and app id will be empty, and the state will be unknown.
void GetBrowserInstanceInfo(const aura::Window* browser_window,
base::UnguessableToken& browser_id,
std::string& browser_app_id,
InstanceState& state) const;
// Updates the browser window status when the web app tab `update` is
// inactivated.
void UpdateBrowserWindowStatus(const InstanceUpdate& update);
void SetWindowActivated(AppType app_type,
AppTypeName app_type_name,
AppTypeNameV2 app_type_name_v2,
const std::string& app_id,
const base::UnguessableToken& instance_id);
void SetWindowInActivated(const std::string& app_id,
const base::UnguessableToken& instance_id,
apps::InstanceState state);
void InitRunningDuration();
void ClearRunningDuration();
// Reads the installed apps from AppRegistryCache before AppPlatformMetrics is
// created to record the install AppKM.
void ReadInstalledApps();
// Records the number of apps of the given `app_type` that the family user has
// recently used.
void RecordAppsCount(AppType app_type);
// Records the app running duration.
void RecordAppsRunningDuration();
// Saves the app usage time metrics UKM to the user preferences and records
// UMA.
void RecordAppsUsageTime();
// Sends the app usage time UKM to `ukm::UkmRecorder`.
void RecordAppsUsageTimeUkm();
// Records the installed app in Chrome OS.
void RecordAppsInstallUkm(const apps::AppUpdate& update,
InstallTime install_time);
// Updates `usage_time_per_two_hours_` each 5 minutes or when the app window
// is inactivated.
void UpdateUsageTime(const base::UnguessableToken& instance_id,
const std::string& app_id,
AppTypeName app_type_name,
const base::TimeDelta& running_time);
// Saves the app window usage time in `usage_time_per_two_hours_` to the user
// pref each 5 minutes.
void SaveUsageTime();
// Reads the app platform metrics saved in the user pref to
// `usage_times_from_pref_`.
void LoadAppsUsageTimeUkmFromPref();
// Sends the app usage time UKM to `ukm::UkmRecorder` based on the usage time
// saved in `usage_times_from_pref_`.
void RecordAppsUsageTimeUkmFromPref();
// Attempts to clear app usage info entries in the pref store for instances if
// and only if both the UKM usage and reporting usage time snapshots have been
// reset.
void CleanUpAppsUsageInfoInPrefStore();
// Clears UKM usage tracked for a given app instance in the pref store.
// Normally triggered after corresponding usage snapshot has been reported to
// UKM for the app instance.
void ClearAppsUsageTimeForInstance(std::string_view instance_id);
void UpdateMetricsBeforeShutdown();
const raw_ptr<Profile> profile_ = nullptr;
const raw_ref<AppRegistryCache> app_registry_cache_;
bool should_record_metrics_on_new_day_ = false;
bool should_refresh_duration_pref = false;
bool should_refresh_activated_count_pref = false;
int user_type_by_device_type_;
BrowserToTabList browser_to_tab_list_;
// |running_start_time_| and |running_duration_| are used for accumulating app
// running duration per each day interval.
std::map<const base::UnguessableToken, RunningStartTime> running_start_time_;
std::map<AppTypeName, base::TimeDelta> running_duration_;
std::map<AppTypeName, int> activated_count_;
// |start_time_per_five_minutes_|, |app_type_running_time_per_five_minutes_|,
// |app_type_v2_running_time_per_five_minutes_| are used for accumulating app
// running duration per 5 minutes interval.
std::map<const base::UnguessableToken, RunningStartTime>
start_time_per_five_minutes_;
std::map<AppTypeName, base::TimeDelta>
app_type_running_time_per_five_minutes_;
std::map<AppTypeNameV2, base::TimeDelta>
app_type_v2_running_time_per_five_minutes_;
// Records the app window running duration for the app usage AppKM.
std::map<const base::UnguessableToken, UsageTime> usage_time_per_two_hours_;
// The app usage time loaded from the user pref during the init phase.
std::vector<std::unique_ptr<UsageTime>> usage_times_from_pref_;
base::ObserverList<Observer> observers_;
base::ScopedObservation<apps::AppRegistryCache,
apps::AppRegistryCache::Observer>
app_registry_cache_observer_{this};
base::ScopedObservation<InstanceRegistry, InstanceRegistry::Observer>
instance_registry_observation_{this};
// Observes `UkmRecorder` only in Managed Guest Session.
base::ScopedObservation<ukm::UkmRecorder, ukm::UkmRecorder::Observer>
ukm_recorder_observer_{this};
};
} // namespace apps
#endif // CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_METRICS_H_