blob: 97f6cb469b4bc3f481604cdc9efb44d39737a706 [file] [log] [blame]
// Copyright 2022 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/metrics/chromeos_system_profile_provider.h"
#include "ash/constants/ash_pref_names.h"
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/files/file_util.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
#include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "components/metrics/structured/recorder.h"
#include "components/prefs/pref_service.h"
#include "components/variations/hashing.h"
#include "third_party/metrics_proto/system_profile.pb.h"
#include "ui/display/display.h"
#include "ui/events/event_utils.h"
namespace {
// Called on a background thread to load cellular device variant
// using ConfigFS.
std::string GetCellularDeviceVariantOnBackgroundThread() {
constexpr char kFirmwareVariantPath[] =
"/run/chromeos-config/v1/modem/firmware-variant";
std::string cellular_device_variant;
const base::FilePath modem_path = base::FilePath(kFirmwareVariantPath);
if (base::PathExists(modem_path)) {
base::ReadFileToString(modem_path, &cellular_device_variant);
}
VLOG(1) << "cellular_device_variant: " << cellular_device_variant;
return cellular_device_variant;
}
bool IsFeatureEnabled(
const ash::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
feature_states_map,
ash::multidevice_setup::mojom::Feature feature) {
return feature_states_map.find(feature)->second ==
ash::multidevice_setup::mojom::FeatureState::kEnabledByUser;
}
} // namespace
ChromeOSSystemProfileProvider::ChromeOSSystemProfileProvider()
: cached_profile_(std::make_unique<metrics::CachedMetricsProfile>()),
weak_ptr_factory_(this) {}
ChromeOSSystemProfileProvider::~ChromeOSSystemProfileProvider() = default;
void ChromeOSSystemProfileProvider::AsyncInit(base::OnceClosure callback) {
base::RepeatingClosure barrier = base::BarrierClosure(4, std::move(callback));
InitTaskGetFullHardwareClass(barrier);
InitTaskGetArcFeatures(barrier);
InitTaskGetTpmFirmwareVersion(barrier);
InitTaskGetCellularDeviceVariant(barrier);
}
void ChromeOSSystemProfileProvider::OnDidCreateMetricsLog() {
registered_user_count_at_log_initialization_ = false;
if (user_manager::UserManager::IsInitialized()) {
registered_user_count_at_log_initialization_ = true;
user_count_at_log_initialization_ =
user_manager::UserManager::Get()->GetLoggedInUsers().size();
}
}
void ChromeOSSystemProfileProvider::ProvideSystemProfileMetrics(
metrics::SystemProfileProto* system_profile_proto) {
WriteLinkedAndroidPhoneProto(system_profile_proto);
UpdateMultiProfileUserCount(system_profile_proto);
WriteDemoModeDimensionMetrics(system_profile_proto);
metrics::SystemProfileProto::Hardware* hardware =
system_profile_proto->mutable_hardware();
hardware->set_full_hardware_class(full_hardware_class_);
display::Display::TouchSupport has_touch =
ui::GetInternalDisplayTouchSupport();
if (has_touch == display::Display::TouchSupport::AVAILABLE)
hardware->set_internal_display_supports_touch(true);
else if (has_touch == display::Display::TouchSupport::UNAVAILABLE)
hardware->set_internal_display_supports_touch(false);
if (tpm_firmware_version_.has_value()) {
hardware->set_tpm_firmware_version(*tpm_firmware_version_);
}
hardware->set_cellular_device_variant(cellular_device_variant_);
if (arc_release_) {
metrics::SystemProfileProto::OS::Arc* arc =
system_profile_proto->mutable_os()->mutable_arc();
arc->set_release(*arc_release_);
}
}
void ChromeOSSystemProfileProvider::WriteLinkedAndroidPhoneProto(
metrics::SystemProfileProto* system_profile_proto) {
ash::multidevice_setup::MultiDeviceSetupClient* client =
ash::multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(
cached_profile_->GetMetricsProfile());
if (!client)
return;
const ash::multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice&
host_status_with_device = client->GetHostStatus();
if (host_status_with_device.first !=
ash::multidevice_setup::mojom::HostStatus::kHostVerified) {
return;
}
metrics::SystemProfileProto::LinkedAndroidPhoneData*
linked_android_phone_data =
system_profile_proto->mutable_linked_android_phone_data();
const uint32_t hashed_name =
variations::HashName(host_status_with_device.second->pii_free_name());
linked_android_phone_data->set_phone_model_name_hash(hashed_name);
const ash::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
feature_states_map = client->GetFeatureStates();
linked_android_phone_data->set_is_smartlock_enabled(IsFeatureEnabled(
feature_states_map, ash::multidevice_setup::mojom::Feature::kSmartLock));
linked_android_phone_data->set_is_instant_tethering_enabled(IsFeatureEnabled(
feature_states_map,
ash::multidevice_setup::mojom::Feature::kInstantTethering));
linked_android_phone_data->set_is_messages_enabled(IsFeatureEnabled(
feature_states_map, ash::multidevice_setup::mojom::Feature::kMessages));
}
void ChromeOSSystemProfileProvider::UpdateMultiProfileUserCount(
metrics::SystemProfileProto* system_profile_proto) {
if (user_manager::UserManager::IsInitialized()) {
size_t user_count =
user_manager::UserManager::Get()->GetLoggedInUsers().size();
// We invalidate the user count if it changed while the log was open.
if (registered_user_count_at_log_initialization_ &&
user_count != user_count_at_log_initialization_) {
user_count = 0;
}
system_profile_proto->set_multi_profile_user_count(user_count);
}
}
void ChromeOSSystemProfileProvider::WriteDemoModeDimensionMetrics(
metrics::SystemProfileProto* system_profile_proto) {
if (!ash::DemoSession::IsDeviceInDemoMode()) {
return;
}
metrics::SystemProfileProto::DemoModeDimensions* demo_mode_dimensions =
system_profile_proto->mutable_demo_mode_dimensions();
PrefService* pref = g_browser_process->local_state();
std::string demo_country = pref->GetString(prefs::kDemoModeCountry);
demo_mode_dimensions->set_country(demo_country);
metrics::SystemProfileProto_DemoModeDimensions_Retailer* retailer =
demo_mode_dimensions->mutable_retailer();
retailer->set_retailer_id(pref->GetString(prefs::kDemoModeRetailerId));
retailer->set_store_id(pref->GetString(prefs::kDemoModeStoreId));
if (chromeos::features::IsCloudGamingDeviceEnabled()) {
demo_mode_dimensions->add_customization_facet(
metrics::
SystemProfileProto_DemoModeDimensions_CustomizationFacet_CLOUD_GAMING_DEVICE);
}
}
void ChromeOSSystemProfileProvider::InitTaskGetFullHardwareClass(
base::OnceClosure callback) {
ash::system::StatisticsProvider::GetInstance()
->ScheduleOnMachineStatisticsLoaded(base::BindOnce(
&ChromeOSSystemProfileProvider::OnMachineStatisticsLoaded,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ChromeOSSystemProfileProvider::InitTaskGetArcFeatures(
base::OnceClosure callback) {
arc::ArcFeaturesParser::GetArcFeatures(
base::BindOnce(&ChromeOSSystemProfileProvider::OnArcFeaturesParsed,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ChromeOSSystemProfileProvider::InitTaskGetTpmFirmwareVersion(
base::OnceClosure callback) {
chromeos::TpmManagerClient::Get()->GetVersionInfo(
tpm_manager::GetVersionInfoRequest(),
base::BindOnce(&ChromeOSSystemProfileProvider::OnTpmManagerGetVersionInfo,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ChromeOSSystemProfileProvider::InitTaskGetCellularDeviceVariant(
base::OnceClosure callback) {
// Run the (potentially expensive) task in the background to avoid blocking
// the UI thread.
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&GetCellularDeviceVariantOnBackgroundThread),
base::BindOnce(&ChromeOSSystemProfileProvider::SetCellularDeviceVariant,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ChromeOSSystemProfileProvider::OnMachineStatisticsLoaded(
base::OnceClosure callback) {
if (const absl::optional<base::StringPiece> full_hardware_class =
ash::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
"hardware_class")) {
full_hardware_class_ = std::string(full_hardware_class.value());
}
std::move(callback).Run();
}
void ChromeOSSystemProfileProvider::OnArcFeaturesParsed(
base::OnceClosure callback,
absl::optional<arc::ArcFeatures> features) {
base::ScopedClosureRunner runner(std::move(callback));
if (!features) {
LOG(WARNING) << "ArcFeatures not available on this build";
return;
}
arc_release_ = features->build_props.at("ro.build.version.release");
}
void ChromeOSSystemProfileProvider::OnTpmManagerGetVersionInfo(
base::OnceClosure callback,
const tpm_manager::GetVersionInfoReply& reply) {
if (reply.status() == tpm_manager::STATUS_SUCCESS) {
tpm_firmware_version_ = reply.firmware_version();
} else {
LOG(ERROR) << "Failed to get TPM version info.";
}
std::move(callback).Run();
}
void ChromeOSSystemProfileProvider::SetCellularDeviceVariant(
base::OnceClosure callback,
std::string cellular_device_variant) {
cellular_device_variant_ = cellular_device_variant;
std::move(callback).Run();
}