blob: 651f640847b51d30562d8c6579f5907812519943 [file] [log] [blame]
// Copyright (c) 2012 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/policy/status_collector/device_status_collector.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <cstdio>
#include <limits>
#include <set>
#include <sstream>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/feature_list.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/status_collector/activity_storage.h"
#include "chrome/browser/chromeos/policy/status_collector/status_collector_state.h"
#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "chromeos/dbus/cryptohome/tpm_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "chromeos/dbus/update_engine_client.h"
#include "chromeos/dbus/util/version_loader.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "chromeos/login/login_state/login_state.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/timezone_settings.h"
#include "chromeos/system/statistics_provider.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/common/enterprise_reporting.mojom.h"
#include "components/arc/session/arc_bridge_service.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/session_manager_types.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "storage/browser/fileapi/external_mount_points.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using base::Time;
using base::TimeDelta;
namespace em = enterprise_management;
namespace {
// How many seconds of inactivity triggers the idle state.
const int kIdleStateThresholdSeconds = 300;
// How much time in the past to store active periods for.
constexpr TimeDelta kMaxStoredPastActivityInterval = TimeDelta::FromDays(30);
// How much time in the future to store active periods for.
constexpr TimeDelta kMaxStoredFutureActivityInterval = TimeDelta::FromDays(2);
// How often, in seconds, to sample the hardware resource usage.
const unsigned int kResourceUsageSampleIntervalSeconds = 120;
// The location we read our CPU statistics from.
const char kProcStat[] = "/proc/stat";
// The location we read our CPU temperature and channel label from.
const char kHwmonDir[] = "/sys/class/hwmon/";
const char kDeviceDir[] = "device";
const char kHwmonDirectoryPattern[] = "hwmon*";
const char kCPUTempFilePattern[] = "temp*_input";
// How often the child's usage time is stored.
static constexpr base::TimeDelta kUpdateChildActiveTimeInterval =
base::TimeDelta::FromSeconds(30);
// Helper function (invoked via blocking pool) to fetch information about
// mounted disks.
std::vector<em::VolumeInfo> GetVolumeInfo(
const std::vector<std::string>& mount_points) {
std::vector<em::VolumeInfo> result;
for (const std::string& mount_point : mount_points) {
base::FilePath mount_path(mount_point);
// Non-native file systems do not have a mount point in the local file
// system. However, it's worth checking here, as it's easier than checking
// earlier which mount point is local, and which one is not.
if (mount_point.empty() || !base::PathExists(mount_path))
continue;
int64_t free_size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
int64_t total_size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
if (free_size < 0 || total_size < 0) {
LOG(ERROR) << "Unable to get volume status for " << mount_point;
continue;
}
em::VolumeInfo info;
info.set_volume_id(mount_point);
info.set_storage_total(total_size);
info.set_storage_free(free_size);
result.push_back(info);
}
return result;
}
// Reads the first CPU line from /proc/stat. Returns an empty string if
// the cpu data could not be read.
// The format of this line from /proc/stat is:
//
// cpu user_time nice_time system_time idle_time
//
// where user_time, nice_time, system_time, and idle_time are all integer
// values measured in jiffies from system startup.
std::string ReadCPUStatistics() {
std::string contents;
if (base::ReadFileToString(base::FilePath(kProcStat), &contents)) {
size_t eol = contents.find("\n");
if (eol != std::string::npos) {
std::string line = contents.substr(0, eol);
if (line.compare(0, 4, "cpu ") == 0)
return line;
}
// First line should always start with "cpu ".
NOTREACHED() << "Could not parse /proc/stat contents: " << contents;
}
LOG(WARNING) << "Unable to read CPU statistics from " << kProcStat;
return std::string();
}
// Read system temperature sensors from
//
// /sys/class/hwmon/hwmon*/(device/)?temp*_input
//
// which contains millidegree Celsius temperature and
//
// /sys/class/hwmon/hwmon*/(device/)?temp*_label or
// /sys/class/hwmon/hwmon*/name
//
// which contains an appropriate label name for the given sensor.
std::vector<em::CPUTempInfo> ReadCPUTempInfo() {
std::vector<em::CPUTempInfo> contents;
// Get directories /sys/class/hwmon/hwmon*
base::FileEnumerator hwmon_enumerator(base::FilePath(kHwmonDir), false,
base::FileEnumerator::DIRECTORIES,
kHwmonDirectoryPattern);
for (base::FilePath hwmon_path = hwmon_enumerator.Next(); !hwmon_path.empty();
hwmon_path = hwmon_enumerator.Next()) {
// Get temp*_input files in hwmon*/ and hwmon*/device/
if (base::PathExists(hwmon_path.Append(kDeviceDir))) {
hwmon_path = hwmon_path.Append(kDeviceDir);
}
base::FileEnumerator enumerator(
hwmon_path, false, base::FileEnumerator::FILES, kCPUTempFilePattern);
for (base::FilePath temperature_path = enumerator.Next();
!temperature_path.empty(); temperature_path = enumerator.Next()) {
// Get appropriate temp*_label file.
std::string label_path = temperature_path.MaybeAsASCII();
if (label_path.empty()) {
LOG(WARNING) << "Unable to parse a path to temp*_input file as ASCII";
continue;
}
base::ReplaceSubstringsAfterOffset(&label_path, 0, "input", "label");
base::FilePath name_path = hwmon_path.Append("name");
// Get the label describing this temperature. Use temp*_label
// if present, fall back on name file or blank.
std::string label;
if (base::PathExists(base::FilePath(label_path))) {
base::ReadFileToString(base::FilePath(label_path), &label);
} else if (base::PathExists(base::FilePath(name_path))) {
base::ReadFileToString(name_path, &label);
} else {
label = std::string();
}
// Read temperature in millidegree Celsius.
std::string temperature_string;
int32_t temperature = 0;
if (base::ReadFileToString(temperature_path, &temperature_string) &&
sscanf(temperature_string.c_str(), "%d", &temperature) == 1) {
// CPU temp in millidegree Celsius to Celsius
temperature /= 1000;
em::CPUTempInfo info;
info.set_cpu_label(label);
info.set_cpu_temp(temperature);
contents.push_back(info);
} else {
LOG(WARNING) << "Unable to read CPU temp from "
<< temperature_path.MaybeAsASCII();
}
}
}
return contents;
}
bool ReadAndroidStatus(
const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
auto* const arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager)
return false;
auto* const instance_holder =
arc_service_manager->arc_bridge_service()->enterprise_reporting();
if (!instance_holder)
return false;
auto* const instance =
ARC_GET_INSTANCE_FOR_METHOD(instance_holder, GetStatus);
if (!instance)
return false;
instance->GetStatus(receiver);
return true;
}
// Converts the given GetTpmStatusReply to TpmStatusInfo.
policy::TpmStatusInfo GetTpmStatusReplyToTpmStatusInfo(
const base::Optional<cryptohome::BaseReply>& reply) {
policy::TpmStatusInfo tpm_status_info;
if (!reply.has_value()) {
LOG(ERROR) << "GetTpmStatus call failed with empty reply.";
return tpm_status_info;
}
if (reply->has_error() &&
reply->error() != cryptohome::CRYPTOHOME_ERROR_NOT_SET) {
LOG(ERROR) << "GetTpmStatus failed with error: " << reply->error();
return tpm_status_info;
}
if (!reply->HasExtension(cryptohome::GetTpmStatusReply::reply)) {
LOG(ERROR)
<< "GetTpmStatus failed with no GetTpmStatusReply extension in reply.";
return tpm_status_info;
}
auto reply_proto = reply->GetExtension(cryptohome::GetTpmStatusReply::reply);
tpm_status_info.enabled = reply_proto.enabled();
tpm_status_info.owned = reply_proto.owned();
tpm_status_info.initialized = reply_proto.initialized();
tpm_status_info.attestation_prepared = reply_proto.attestation_prepared();
tpm_status_info.attestation_enrolled = reply_proto.attestation_enrolled();
tpm_status_info.dictionary_attack_counter =
reply_proto.dictionary_attack_counter();
tpm_status_info.dictionary_attack_threshold =
reply_proto.dictionary_attack_threshold();
tpm_status_info.dictionary_attack_lockout_in_effect =
reply_proto.dictionary_attack_lockout_in_effect();
tpm_status_info.dictionary_attack_lockout_seconds_remaining =
reply_proto.dictionary_attack_lockout_seconds_remaining();
tpm_status_info.boot_lockbox_finalized = reply_proto.boot_lockbox_finalized();
return tpm_status_info;
}
void ReadTpmStatus(policy::DeviceStatusCollector::TpmStatusReceiver callback) {
// D-Bus calls are allowed only on the UI thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
chromeos::CryptohomeClient::Get()->GetTpmStatus(
cryptohome::GetTpmStatusRequest(),
base::BindOnce(
[](policy::DeviceStatusCollector::TpmStatusReceiver callback,
base::Optional<cryptohome::BaseReply> reply) {
std::move(callback).Run(GetTpmStatusReplyToTpmStatusInfo(reply));
},
std::move(callback)));
}
base::Version GetPlatformVersion() {
return base::Version(base::SysInfo::OperatingSystemVersion());
}
// Helper routine to convert from Shill-provided signal strength (percent)
// to dBm units expected by server.
int ConvertWifiSignalStrength(int signal_strength) {
// Shill attempts to convert WiFi signal strength from its internal dBm to a
// percentage range (from 0-100) by adding 120 to the raw dBm value,
// and then clamping the result to the range 0-100 (see
// shill::WiFiService::SignalToStrength()).
//
// To convert back to dBm, we subtract 120 from the percentage value to yield
// a clamped dBm value in the range of -119 to -20dBm.
//
// TODO(atwilson): Tunnel the raw dBm signal strength from Shill instead of
// doing the conversion here so we can report non-clamped values
// (crbug.com/463334).
DCHECK_GT(signal_strength, 0);
DCHECK_LE(signal_strength, 100);
return signal_strength - 120;
}
bool IsKioskApp() {
auto user_type = chromeos::LoginState::Get()->GetLoggedInUserType();
return user_type == chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP ||
user_type == chromeos::LoginState::LOGGED_IN_USER_ARC_KIOSK_APP;
}
// Utility method to turn cpu_temp_fetcher_ to OnceCallback
std::vector<em::CPUTempInfo> InvokeCpuTempFetcher(
policy::DeviceStatusCollector::CPUTempFetcher fetcher) {
return fetcher.Run();
}
} // namespace
namespace policy {
class DeviceStatusCollectorState : public StatusCollectorState {
public:
explicit DeviceStatusCollectorState(
const scoped_refptr<base::SequencedTaskRunner> task_runner,
const StatusCollectorCallback& response)
: StatusCollectorState(task_runner, response) {}
// Queues an async callback to query disk volume information.
void SampleVolumeInfo(
const DeviceStatusCollector::VolumeInfoFetcher& volume_info_fetcher) {
// Create list of mounted disk volumes to query status.
std::vector<storage::MountPoints::MountPointInfo> external_mount_points;
storage::ExternalMountPoints::GetSystemInstance()->AddMountPointInfosTo(
&external_mount_points);
std::vector<std::string> mount_points;
for (const auto& info : external_mount_points)
mount_points.push_back(info.path.value());
for (const auto& mount_info :
chromeos::disks::DiskMountManager::GetInstance()->mount_points()) {
// Extract a list of mount points to populate.
mount_points.push_back(mount_info.first);
}
// Call out to the blocking pool to sample disk volume info.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::Bind(volume_info_fetcher, mount_points),
base::Bind(&DeviceStatusCollectorState::OnVolumeInfoReceived, this));
}
// Queues an async callback to query CPU temperature information.
void SampleCPUTempInfo(
const DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher) {
// Call out to the blocking pool to sample CPU temp.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
cpu_temp_fetcher,
base::Bind(&DeviceStatusCollectorState::OnCPUTempInfoReceived, this));
}
bool FetchAndroidStatus(const DeviceStatusCollector::AndroidStatusFetcher&
android_status_fetcher) {
return android_status_fetcher.Run(
base::Bind(&DeviceStatusCollectorState::OnAndroidInfoReceived, this));
}
void FetchTpmStatus(
const DeviceStatusCollector::TpmStatusFetcher& tpm_status_fetcher) {
tpm_status_fetcher.Run(
base::BindOnce(&DeviceStatusCollectorState::OnTpmStatusReceived, this));
}
void FetchProbeData(
DeviceStatusCollector::ProbeDataFetcher probe_data_fetcher) {
std::move(probe_data_fetcher)
.Run(base::BindOnce(&DeviceStatusCollectorState::OnProbeDataReceived,
this));
}
private:
~DeviceStatusCollectorState() override = default;
void OnVolumeInfoReceived(const std::vector<em::VolumeInfo>& volume_info) {
response_params_.device_status->clear_volume_infos();
for (const em::VolumeInfo& info : volume_info)
*response_params_.device_status->add_volume_infos() = info;
}
void OnCPUTempInfoReceived(
const std::vector<em::CPUTempInfo>& cpu_temp_info) {
// Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be
// called.
DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0);
DLOG_IF(WARNING, cpu_temp_info.empty())
<< "Unable to read CPU temp information.";
base::Time timestamp = base::Time::Now();
for (const em::CPUTempInfo& info : cpu_temp_info) {
auto* new_info = response_params_.device_status->add_cpu_temp_infos();
*new_info = info;
new_info->set_timestamp(timestamp.ToJavaTime());
}
}
void OnAndroidInfoReceived(const std::string& status,
const std::string& droid_guard_info) {
em::AndroidStatus* const android_status =
response_params_.session_status->mutable_android_status();
android_status->set_status_payload(status);
android_status->set_droid_guard_info(droid_guard_info);
}
void OnTpmStatusReceived(const TpmStatusInfo& tpm_status_struct) {
// Make sure we edit the state on the right thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
em::TpmStatusInfo* const tpm_status_proto =
response_params_.device_status->mutable_tpm_status_info();
tpm_status_proto->set_enabled(tpm_status_struct.enabled);
tpm_status_proto->set_owned(tpm_status_struct.owned);
tpm_status_proto->set_tpm_initialized(tpm_status_struct.initialized);
tpm_status_proto->set_attestation_prepared(
tpm_status_struct.attestation_prepared);
tpm_status_proto->set_attestation_enrolled(
tpm_status_struct.attestation_enrolled);
tpm_status_proto->set_dictionary_attack_counter(
tpm_status_struct.dictionary_attack_counter);
tpm_status_proto->set_dictionary_attack_threshold(
tpm_status_struct.dictionary_attack_threshold);
tpm_status_proto->set_dictionary_attack_lockout_in_effect(
tpm_status_struct.dictionary_attack_lockout_in_effect);
tpm_status_proto->set_dictionary_attack_lockout_seconds_remaining(
tpm_status_struct.dictionary_attack_lockout_seconds_remaining);
tpm_status_proto->set_boot_lockbox_finalized(
tpm_status_struct.boot_lockbox_finalized);
}
void OnProbeDataReceived(
const base::Optional<runtime_probe::ProbeResult>& probe_result,
const base::circular_deque<std::unique_ptr<SampledData>>& samples) {
// Make sure we edit the state on the right thread.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be
// called.
DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0);
// Store CPU measurement samples.
for (const std::unique_ptr<SampledData>& sample_data : samples) {
for (auto it = sample_data->cpu_samples.begin();
it != sample_data->cpu_samples.end(); it++) {
auto* new_info = response_params_.device_status->add_cpu_temp_infos();
*new_info = it->second;
}
}
if (!probe_result.has_value())
return;
if (probe_result.value().error() !=
runtime_probe::RUNTIME_PROBE_ERROR_NOT_SET) {
return;
}
if (probe_result.value().battery_size() > 0) {
em::PowerStatus* const power_status =
response_params_.device_status->mutable_power_status();
for (const auto& battery : probe_result.value().battery()) {
em::BatteryInfo* const battery_info = power_status->add_batteries();
battery_info->set_serial(battery.values().serial_number());
battery_info->set_manufacturer(battery.values().manufacturer());
battery_info->set_cycle_count(battery.values().cycle_count_smart());
// uAh to mAh
battery_info->set_design_capacity(
battery.values().charge_full_design() / 1000);
battery_info->set_full_charge_capacity(battery.values().charge_full() /
1000);
// uV to mV:
battery_info->set_design_min_voltage(
battery.values().voltage_min_design() / 1000);
for (const std::unique_ptr<SampledData>& sample_data : samples) {
auto it = sample_data->battery_samples.find(battery.name());
if (it != sample_data->battery_samples.end())
battery_info->add_samples()->CheckTypeAndMergeFrom(it->second);
}
}
}
if (probe_result.value().storage_size() > 0) {
em::StorageStatus* const storage_status =
response_params_.device_status->mutable_storage_status();
for (const auto& storage : probe_result.value().storage()) {
em::DiskInfo* const disk_info = storage_status->add_disks();
disk_info->set_serial(base::NumberToString(storage.values().serial()));
disk_info->set_manufacturer(
base::NumberToString(storage.values().manfid()));
disk_info->set_model(storage.values().name());
disk_info->set_type(storage.values().type());
disk_info->set_size(storage.values().size());
}
}
}
};
TpmStatusInfo::TpmStatusInfo() = default;
TpmStatusInfo::TpmStatusInfo(const TpmStatusInfo&) = default;
TpmStatusInfo::TpmStatusInfo(
bool enabled,
bool owned,
bool initialized,
bool attestation_prepared,
bool attestation_enrolled,
int32_t dictionary_attack_counter,
int32_t dictionary_attack_threshold,
bool dictionary_attack_lockout_in_effect,
int32_t dictionary_attack_lockout_seconds_remaining,
bool boot_lockbox_finalized)
: enabled(enabled),
owned(owned),
initialized(initialized),
attestation_prepared(attestation_prepared),
attestation_enrolled(attestation_enrolled),
dictionary_attack_counter(dictionary_attack_counter),
dictionary_attack_threshold(dictionary_attack_threshold),
dictionary_attack_lockout_in_effect(dictionary_attack_lockout_in_effect),
dictionary_attack_lockout_seconds_remaining(
dictionary_attack_lockout_seconds_remaining),
boot_lockbox_finalized(boot_lockbox_finalized) {}
TpmStatusInfo::~TpmStatusInfo() = default;
SampledData::SampledData() = default;
SampledData::~SampledData() = default;
DeviceStatusCollector::DeviceStatusCollector(
PrefService* pref_service,
chromeos::system::StatisticsProvider* provider,
const VolumeInfoFetcher& volume_info_fetcher,
const CPUStatisticsFetcher& cpu_statistics_fetcher,
const CPUTempFetcher& cpu_temp_fetcher,
const AndroidStatusFetcher& android_status_fetcher,
const TpmStatusFetcher& tpm_status_fetcher,
TimeDelta activity_day_start,
bool is_enterprise_reporting)
: max_stored_past_activity_interval_(kMaxStoredPastActivityInterval),
max_stored_future_activity_interval_(kMaxStoredFutureActivityInterval),
pref_service_(pref_service),
last_idle_check_(Time()),
last_active_check_(base::Time()),
last_state_active_(true),
volume_info_fetcher_(volume_info_fetcher),
cpu_statistics_fetcher_(cpu_statistics_fetcher),
cpu_temp_fetcher_(cpu_temp_fetcher),
android_status_fetcher_(android_status_fetcher),
tpm_status_fetcher_(tpm_status_fetcher),
statistics_provider_(provider),
cros_settings_(chromeos::CrosSettings::Get()),
power_manager_(chromeos::PowerManagerClient::Get()),
session_manager_(session_manager::SessionManager::Get()),
runtime_probe_(
chromeos::DBusThreadManager::Get()->GetRuntimeProbeClient()),
is_enterprise_reporting_(is_enterprise_reporting),
activity_day_start_(activity_day_start),
task_runner_(nullptr),
weak_factory_(this) {
// Get the task runner of the current thread, so we can queue status responses
// on this thread.
CHECK(base::SequencedTaskRunnerHandle::IsSet());
task_runner_ = base::SequencedTaskRunnerHandle::Get();
if (volume_info_fetcher_.is_null())
volume_info_fetcher_ = base::Bind(&GetVolumeInfo);
if (cpu_statistics_fetcher_.is_null())
cpu_statistics_fetcher_ = base::Bind(&ReadCPUStatistics);
if (cpu_temp_fetcher_.is_null())
cpu_temp_fetcher_ = base::Bind(&ReadCPUTempInfo);
if (android_status_fetcher_.is_null())
android_status_fetcher_ = base::Bind(&ReadAndroidStatus);
if (tpm_status_fetcher_.is_null())
tpm_status_fetcher_ = base::BindRepeating(&ReadTpmStatus);
if (probe_data_fetcher_.is_null())
probe_data_fetcher_ = base::BindRepeating(
&DeviceStatusCollector::FetchProbeData, weak_factory_.GetWeakPtr());
idle_poll_timer_.Start(FROM_HERE,
TimeDelta::FromSeconds(kIdlePollIntervalSeconds), this,
&DeviceStatusCollector::CheckIdleState);
update_child_usage_timer_.Start(FROM_HERE, kUpdateChildActiveTimeInterval,
this,
&DeviceStatusCollector::UpdateChildUsageTime);
resource_usage_sampling_timer_.Start(
FROM_HERE, TimeDelta::FromSeconds(kResourceUsageSampleIntervalSeconds),
this, &DeviceStatusCollector::SampleResourceUsage);
// Watch for changes to the individual policies that control what the status
// reports contain.
base::Closure callback = base::Bind(
&DeviceStatusCollector::UpdateReportingSettings, base::Unretained(this));
version_info_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceVersionInfo, callback);
activity_times_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceActivityTimes, callback);
boot_mode_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceBootMode, callback);
network_interfaces_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceNetworkInterfaces, callback);
users_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceUsers, callback);
hardware_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceHardwareStatus, callback);
session_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceSessionStatus, callback);
os_update_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportOsUpdateStatus, callback);
running_kiosk_app_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportRunningKioskApp, callback);
power_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDevicePowerStatus, callback);
storage_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceStorageStatus, callback);
board_status_subscription_ = cros_settings_->AddSettingsObserver(
chromeos::kReportDeviceBoardStatus, callback);
// Watch for changes on the device state to calculate the child's active time.
power_manager_->AddObserver(this);
if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
chromeos::UsageTimeStateNotifier::GetInstance()->AddObserver(this);
} else {
session_manager_->AddObserver(this);
}
// Fetch the current values of the policies.
UpdateReportingSettings();
// Get the OS, firmware, and TPM version info.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::Bind(&chromeos::version_loader::GetVersion,
chromeos::version_loader::VERSION_FULL),
base::Bind(&DeviceStatusCollector::OnOSVersion,
weak_factory_.GetWeakPtr()));
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::Bind(&chromeos::version_loader::GetFirmware),
base::Bind(&DeviceStatusCollector::OnOSFirmware,
weak_factory_.GetWeakPtr()));
chromeos::tpm_util::GetTpmVersion(base::BindOnce(
&DeviceStatusCollector::OnTpmVersion, weak_factory_.GetWeakPtr()));
// If doing enterprise device-level reporting, observe the list of users to be
// reported. Consumer reporting is enforced for the signed-in registered user
// therefore this preference is not observed.
if (is_enterprise_reporting_) {
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(pref_service_);
pref_change_registrar_->Add(
prefs::kReportingUsers,
base::BindRepeating(&DeviceStatusCollector::ReportingUsersChanged,
weak_factory_.GetWeakPtr()));
}
DCHECK(pref_service_->GetInitializationStatus() !=
PrefService::INITIALIZATION_STATUS_WAITING);
activity_storage_ = std::make_unique<ActivityStorage>(
pref_service_,
(is_enterprise_reporting_ ? prefs::kDeviceActivityTimes
: prefs::kUserActivityTimes),
activity_day_start, is_enterprise_reporting_);
}
DeviceStatusCollector::~DeviceStatusCollector() {
power_manager_->RemoveObserver(this);
if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier)) {
chromeos::UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
} else {
session_manager_->RemoveObserver(this);
}
}
// static
void DeviceStatusCollector::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(prefs::kDeviceActivityTimes);
}
// static
void DeviceStatusCollector::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kReportArcStatusEnabled, false);
registry->RegisterDictionaryPref(prefs::kUserActivityTimes);
registry->RegisterTimePref(prefs::kLastChildScreenTimeReset, Time());
registry->RegisterTimePref(prefs::kLastChildScreenTimeSaved, Time());
registry->RegisterIntegerPref(prefs::kChildScreenTimeMilliseconds, 0);
}
TimeDelta DeviceStatusCollector::GetActiveChildScreenTime() {
if (!user_manager::UserManager::Get()->IsLoggedInAsChildUser())
return TimeDelta::FromSeconds(0);
UpdateChildUsageTime();
return TimeDelta::FromMilliseconds(
pref_service_->GetInteger(prefs::kChildScreenTimeMilliseconds));
}
void DeviceStatusCollector::CheckIdleState() {
ProcessIdleState(ui::CalculateIdleState(kIdleStateThresholdSeconds));
}
void DeviceStatusCollector::UpdateReportingSettings() {
// Attempt to fetch the current value of the reporting settings.
// If trusted values are not available, register this function to be called
// back when they are available.
if (chromeos::CrosSettingsProvider::TRUSTED !=
cros_settings_->PrepareTrustedValues(
base::Bind(&DeviceStatusCollector::UpdateReportingSettings,
weak_factory_.GetWeakPtr()))) {
return;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceVersionInfo,
&report_version_info_)) {
report_version_info_ = true;
}
if (!is_enterprise_reporting_) {
// Only report activity times for consumer if time limit is enabled.
report_activity_times_ =
base::FeatureList::IsEnabled(features::kUsageTimeLimitPolicy);
} else if (!cros_settings_->GetBoolean(chromeos::kReportDeviceActivityTimes,
&report_activity_times_)) {
report_activity_times_ = true;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceBootMode,
&report_boot_mode_)) {
report_boot_mode_ = true;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceSessionStatus,
&report_kiosk_session_status_)) {
report_kiosk_session_status_ = true;
}
// Network interfaces are reported for enterprise devices only by default.
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceNetworkInterfaces,
&report_network_interfaces_)) {
report_network_interfaces_ = is_enterprise_reporting_;
}
// Device users are reported for enterprise devices only by default.
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceUsers,
&report_users_)) {
report_users_ = is_enterprise_reporting_;
}
// Hardware status is reported for enterprise devices only by default.
const bool already_reporting_hardware_status = report_hardware_status_;
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceHardwareStatus,
&report_hardware_status_)) {
report_hardware_status_ = is_enterprise_reporting_;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDevicePowerStatus,
&report_power_status_)) {
report_power_status_ = false;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceStorageStatus,
&report_storage_status_)) {
report_storage_status_ = false;
}
if (!cros_settings_->GetBoolean(chromeos::kReportDeviceBoardStatus,
&report_board_status_)) {
report_board_status_ = false;
}
if (!report_hardware_status_) {
ClearCachedResourceUsage();
} else if (!already_reporting_hardware_status) {
// Turning on hardware status reporting - fetch an initial sample
// immediately instead of waiting for the sampling timer to fire.
SampleResourceUsage();
}
// Os update status and running kiosk app reporting are disabled by default.
if (!cros_settings_->GetBoolean(chromeos::kReportOsUpdateStatus,
&report_os_update_status_)) {
report_os_update_status_ = false;
}
if (!cros_settings_->GetBoolean(chromeos::kReportRunningKioskApp,
&report_running_kiosk_app_)) {
report_running_kiosk_app_ = false;
}
}
Time DeviceStatusCollector::GetCurrentTime() {
return Time::Now();
}
void DeviceStatusCollector::ClearCachedResourceUsage() {
resource_usage_.clear();
last_cpu_active_ = 0;
last_cpu_idle_ = 0;
}
void DeviceStatusCollector::ProcessIdleState(ui::IdleState state) {
// Do nothing if device activity reporting is disabled or if it's a child
// account. Usage time for child accounts are calculated differently.
if (!report_activity_times_ || !is_enterprise_reporting_ ||
user_manager::UserManager::Get()->IsLoggedInAsChildUser()) {
return;
}
Time now = GetCurrentTime();
// For kiosk apps we report total uptime instead of active time.
if (state == ui::IDLE_STATE_ACTIVE || IsKioskApp()) {
CHECK(is_enterprise_reporting_);
std::string user_email = GetUserForActivityReporting();
// If it's been too long since the last report, or if the activity is
// negative (which can happen when the clock changes), assume a single
// interval of activity.
int active_seconds = (now - last_idle_check_).InSeconds();
if (active_seconds < 0 ||
active_seconds >= static_cast<int>((2 * kIdlePollIntervalSeconds))) {
activity_storage_->AddActivityPeriod(
now - TimeDelta::FromSeconds(kIdlePollIntervalSeconds), now, now,
user_email);
} else {
activity_storage_->AddActivityPeriod(last_idle_check_, now, now,
user_email);
}
activity_storage_->PruneActivityPeriods(
now, max_stored_past_activity_interval_,
max_stored_future_activity_interval_);
}
last_idle_check_ = now;
}
void DeviceStatusCollector::OnSessionStateChanged() {
UpdateChildUsageTime();
last_state_active_ =
session_manager::SessionManager::Get()->session_state() ==
session_manager::SessionState::ACTIVE;
}
void DeviceStatusCollector::OnUsageTimeStateChange(
chromeos::UsageTimeStateNotifier::UsageTimeState state) {
UpdateChildUsageTime();
last_state_active_ =
state == chromeos::UsageTimeStateNotifier::UsageTimeState::ACTIVE;
}
void DeviceStatusCollector::ScreenIdleStateChanged(
const power_manager::ScreenIdleState& state) {
// This logic are going to be done by OnUsageTimeStateChange method if
// UsageTimeStateNotifier feature is enabled.
if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
return;
UpdateChildUsageTime();
// It is active if screen is on and if the session is also active.
last_state_active_ =
!state.off() && session_manager_->session_state() ==
session_manager::SessionState::ACTIVE;
}
void DeviceStatusCollector::SuspendImminent(
power_manager::SuspendImminent::Reason reason) {
// This logic are going to be done by OnUsageTimeStateChange method if
// UsageTimeStateNotifier feature is enabled.
if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
return;
UpdateChildUsageTime();
// Device is going to be suspeded, so it won't be active.
last_state_active_ = false;
}
void DeviceStatusCollector::SuspendDone(const base::TimeDelta& sleep_duration) {
// This logic are going to be done by OnUsageTimeStateChange method if
// UsageTimeStateNotifier feature is enabled.
if (base::FeatureList::IsEnabled(features::kUsageTimeStateNotifier))
return;
UpdateChildUsageTime();
// Device is returning from suspension, so it is considered active if the
// session is also active.
last_state_active_ = session_manager_->session_state() ==
session_manager::SessionState::ACTIVE;
}
void DeviceStatusCollector::PowerChanged(
const power_manager::PowerSupplyProperties& prop) {
if (!power_status_callback_.is_null())
std::move(power_status_callback_).Run(prop);
}
void DeviceStatusCollector::UpdateChildUsageTime() {
if (!report_activity_times_ ||
!user_manager::UserManager::Get()->IsLoggedInAsChildUser()) {
return;
}
// Only child accounts should be using this method.
CHECK(user_manager::UserManager::Get()->IsLoggedInAsChildUser());
Time now = GetCurrentTime();
Time reset_time = now.LocalMidnight() + activity_day_start_;
if (reset_time > now)
reset_time -= TimeDelta::FromDays(1);
// Reset screen time if it has not been reset today.
if (reset_time > pref_service_->GetTime(prefs::kLastChildScreenTimeReset)) {
pref_service_->SetTime(prefs::kLastChildScreenTimeReset, now);
pref_service_->SetInteger(prefs::kChildScreenTimeMilliseconds, 0);
pref_service_->CommitPendingWrite();
}
if (!last_active_check_.is_null() && last_state_active_) {
// If it's been too long since the last report, or if the activity is
// negative (which can happen when the clock changes), assume a single
// interval of activity. This is the same strategy used to enterprise users.
base::TimeDelta active_seconds = now - last_active_check_;
if (active_seconds < base::TimeDelta::FromSeconds(0) ||
active_seconds >= (2 * kUpdateChildActiveTimeInterval)) {
activity_storage_->AddActivityPeriod(now - kUpdateChildActiveTimeInterval,
now, now,
GetUserForActivityReporting());
} else {
activity_storage_->AddActivityPeriod(last_active_check_, now, now,
GetUserForActivityReporting());
}
activity_storage_->PruneActivityPeriods(
now, max_stored_past_activity_interval_,
max_stored_future_activity_interval_);
}
last_active_check_ = now;
}
void DeviceStatusCollector::SampleResourceUsage() {
// Results must be written in the creation thread since that's where they
// are read from in the Get*StatusAsync methods.
DCHECK(thread_checker_.CalledOnValidThread());
// If hardware reporting has been disabled, do nothing here.
if (!report_hardware_status_)
return;
// Call out to the blocking pool to sample CPU stats.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
cpu_statistics_fetcher_,
base::Bind(&DeviceStatusCollector::ReceiveCPUStatistics,
weak_factory_.GetWeakPtr(), base::Time::Now()));
}
void DeviceStatusCollector::ReceiveCPUStatistics(const base::Time& timestamp,
const std::string& stats) {
int cpu_usage_percent = 0;
if (stats.empty()) {
DLOG(WARNING) << "Unable to read CPU statistics";
} else {
// Parse the data from /proc/stat, whose format is defined at
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt.
//
// The CPU usage values in /proc/stat are measured in the imprecise unit
// "jiffies", but we just care about the relative magnitude of "active" vs
// "idle" so the exact value of a jiffy is irrelevant.
//
// An example value for this line:
//
// cpu 123 456 789 012 345 678
//
// We only care about the first four numbers: user_time, nice_time,
// sys_time, and idle_time.
uint64_t user = 0, nice = 0, system = 0, idle = 0;
int vals = sscanf(stats.c_str(),
"cpu %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, &user,
&nice, &system, &idle);
DCHECK_EQ(4, vals);
// The values returned from /proc/stat are cumulative totals, so calculate
// the difference between the last sample and this one.
uint64_t active = user + nice + system;
uint64_t total = active + idle;
uint64_t last_total = last_cpu_active_ + last_cpu_idle_;
DCHECK_GE(active, last_cpu_active_);
DCHECK_GE(idle, last_cpu_idle_);
DCHECK_GE(total, last_total);
if ((total - last_total) > 0) {
cpu_usage_percent =
(100 * (active - last_cpu_active_)) / (total - last_total);
}
last_cpu_active_ = active;
last_cpu_idle_ = idle;
}
DCHECK_LE(cpu_usage_percent, 100);
ResourceUsage usage = {cpu_usage_percent,
base::SysInfo::AmountOfAvailablePhysicalMemory()};
resource_usage_.push_back(usage);
// If our cache of samples is full, throw out old samples to make room for new
// sample.
if (resource_usage_.size() > kMaxResourceUsageSamples)
resource_usage_.pop_front();
std::unique_ptr<SampledData> sample = std::make_unique<SampledData>();
sample->timestamp = base::Time::Now();
if (report_power_status_) {
runtime_probe::ProbeRequest request;
request.add_categories(runtime_probe::ProbeRequest::battery);
runtime_probe_->ProbeCategories(
request, base::BindOnce(&DeviceStatusCollector::SampleProbeData,
weak_factory_.GetWeakPtr(), std::move(sample),
SamplingProbeResultCallback()));
} else {
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&InvokeCpuTempFetcher, cpu_temp_fetcher_),
base::BindOnce(&DeviceStatusCollector::ReceiveCPUTemperature,
weak_factory_.GetWeakPtr(), std::move(sample),
SamplingCallback()));
}
}
void DeviceStatusCollector::SampleProbeData(
std::unique_ptr<SampledData> sample,
SamplingProbeResultCallback callback,
base::Optional<runtime_probe::ProbeResult> result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!result.has_value())
return;
if (result.value().error() != runtime_probe::RUNTIME_PROBE_ERROR_NOT_SET)
return;
if (result.value().battery_size() == 0)
return;
for (const auto& battery : result.value().battery()) {
enterprise_management::BatterySample battery_sample;
battery_sample.set_timestamp(sample->timestamp.ToJavaTime());
// Convert uV to mV
battery_sample.set_voltage(battery.values().voltage_now() / 1000);
// Convert uAh to mAh
battery_sample.set_remaining_capacity(battery.values().charge_now() / 1000);
// Convert 0.1 Kelvin to Celsius
battery_sample.set_temperature(
(battery.values().temperature_smart() - 2731) / 10);
sample->battery_samples[battery.name()] = battery_sample;
}
SamplingCallback completion_callback;
if (!callback.is_null())
completion_callback = base::BindOnce(std::move(callback), result);
// PowerManagerClient::Observer::PowerChanged can be called as a result of
// power_manager_->RequestStatusUpdate() as well as for other reasons,
// so we store power_status_callback_ here instead of triggering
// SampleDischargeRate from PowerChanged().
DCHECK(power_status_callback_.is_null()); // Previous sampling is completed.
power_status_callback_ = base::BindOnce(
&DeviceStatusCollector::SampleDischargeRate, weak_factory_.GetWeakPtr(),
std::move(sample), std::move(completion_callback));
power_manager_->RequestStatusUpdate();
}
void DeviceStatusCollector::SampleDischargeRate(
std::unique_ptr<SampledData> sample,
SamplingCallback callback,
const power_manager::PowerSupplyProperties& prop) {
if (prop.has_battery_discharge_rate()) {
int discharge_rate_mW =
static_cast<int>(prop.battery_discharge_rate() * 1000);
for (auto it = sample->battery_samples.begin();
it != sample->battery_samples.end(); it++) {
it->second.set_discharge_rate(discharge_rate_mW);
}
}
if (prop.has_battery_percent() && prop.battery_percent() >= 0) {
int percent = static_cast<int>(prop.battery_percent());
for (auto it = sample->battery_samples.begin();
it != sample->battery_samples.end(); it++) {
it->second.set_charge_rate(percent);
}
}
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&InvokeCpuTempFetcher, cpu_temp_fetcher_),
base::BindOnce(&DeviceStatusCollector::ReceiveCPUTemperature,
weak_factory_.GetWeakPtr(), std::move(sample),
std::move(callback)));
}
void DeviceStatusCollector::ReceiveCPUTemperature(
std::unique_ptr<SampledData> sample,
SamplingCallback callback,
std::vector<em::CPUTempInfo> measurements) {
auto timestamp = sample->timestamp.ToJavaTime();
for (const auto& measurement : measurements) {
sample->cpu_samples[measurement.cpu_label()] = measurement;
sample->cpu_samples[measurement.cpu_label()].set_timestamp(timestamp);
}
AddDataSample(std::move(sample), std::move(callback));
}
void DeviceStatusCollector::AddDataSample(std::unique_ptr<SampledData> sample,
SamplingCallback callback) {
sampled_data_.push_back(std::move(sample));
// If our cache of samples is full, throw out old samples to make room for new
// sample.
if (sampled_data_.size() > kMaxResourceUsageSamples)
sampled_data_.pop_front();
// We have two code paths that end here. One is regular sampling, that does
// not have final callback, and full report request, that would use callback
// to receive ProbeResponse.
if (!callback.is_null())
std::move(callback).Run();
}
void DeviceStatusCollector::FetchProbeData(ProbeDataReceiver callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
runtime_probe::ProbeRequest request;
if (report_power_status_)
request.add_categories(runtime_probe::ProbeRequest::battery);
if (report_storage_status_)
request.add_categories(runtime_probe::ProbeRequest::storage);
auto sample = std::make_unique<SampledData>();
sample->timestamp = base::Time::Now();
auto completion_callback =
base::BindOnce(&DeviceStatusCollector::OnProbeDataFetched,
weak_factory_.GetWeakPtr(), std::move(callback));
runtime_probe_->ProbeCategories(
request, base::BindOnce(&DeviceStatusCollector::SampleProbeData,
weak_factory_.GetWeakPtr(), std::move(sample),
std::move(completion_callback)));
}
void DeviceStatusCollector::OnProbeDataFetched(
ProbeDataReceiver callback,
base::Optional<runtime_probe::ProbeResult> reply) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
std::move(callback).Run(reply, sampled_data_);
}
void DeviceStatusCollector::ReportingUsersChanged() {
std::vector<std::string> reporting_users;
for (auto& value :
pref_service_->GetList(prefs::kReportingUsers)->GetList()) {
if (value.is_string())
reporting_users.push_back(value.GetString());
}
activity_storage_->FilterActivityPeriodsByUsers(reporting_users);
}
std::string DeviceStatusCollector::GetUserForActivityReporting() const {
// Primary user is used as unique identifier of a single session, even for
// multi-user sessions.
const user_manager::User* const primary_user =
user_manager::UserManager::Get()->GetPrimaryUser();
if (!primary_user || !primary_user->HasGaiaAccount())
return std::string();
// Report only affiliated users for enterprise reporting and signed-in user
// for consumer reporting.
std::string primary_user_email = primary_user->GetAccountId().GetUserEmail();
if (is_enterprise_reporting_ &&
!chromeos::ChromeUserManager::Get()->ShouldReportUser(
primary_user_email)) {
return std::string();
}
return primary_user_email;
}
bool DeviceStatusCollector::IncludeEmailsInActivityReports() const {
// In enterprise reporting including users' emails depends on
// kReportDeviceUsers preference. In consumer reporting only current user is
// reported and email address is always included.
return !is_enterprise_reporting_ || report_users_;
}
bool DeviceStatusCollector::GetActivityTimes(
em::DeviceStatusReportRequest* status) {
if (user_manager::UserManager::Get()->IsLoggedInAsChildUser()) {
UpdateChildUsageTime();
}
// If user reporting is off, data should be aggregated per day.
// Signed-in user is reported in non-enterprise reporting.
std::vector<ActivityStorage::ActivityPeriod> activity_times =
activity_storage_->GetFilteredActivityPeriods(
!IncludeEmailsInActivityReports());
bool anything_reported = false;
for (const auto& activity_period : activity_times) {
// This is correct even when there are leap seconds, because when a leap
// second occurs, two consecutive seconds have the same timestamp.
int64_t end_timestamp =
activity_period.start_timestamp + Time::kMillisecondsPerDay;
em::ActiveTimePeriod* active_period = status->add_active_periods();
em::TimePeriod* period = active_period->mutable_time_period();
period->set_start_timestamp(activity_period.start_timestamp);
period->set_end_timestamp(end_timestamp);
active_period->set_active_duration(activity_period.activity_milliseconds);
// Report user email only if users reporting is turned on.
if (!activity_period.user_email.empty())
active_period->set_user_email(activity_period.user_email);
if (activity_period.start_timestamp >= last_reported_day_) {
last_reported_day_ = activity_period.start_timestamp;
duration_for_last_reported_day_ = activity_period.activity_milliseconds;
}
anything_reported = true;
}
return anything_reported;
}
bool DeviceStatusCollector::GetVersionInfo(
em::DeviceStatusReportRequest* status) {
status->set_os_version(os_version_);
if (!is_enterprise_reporting_)
return true;
// Enterprise-only version reporting below.
status->set_browser_version(version_info::GetVersionNumber());
status->set_channel(ConvertToProtoChannel(chrome::GetChannel()));
status->set_firmware_version(firmware_version_);
em::TpmVersionInfo* const tpm_version_info =
status->mutable_tpm_version_info();
tpm_version_info->set_family(tpm_version_info_.family);
tpm_version_info->set_spec_level(tpm_version_info_.spec_level);
tpm_version_info->set_manufacturer(tpm_version_info_.manufacturer);
tpm_version_info->set_tpm_model(tpm_version_info_.tpm_model);
tpm_version_info->set_firmware_version(tpm_version_info_.firmware_version);
tpm_version_info->set_vendor_specific(tpm_version_info_.vendor_specific);
return true;
}
bool DeviceStatusCollector::GetBootMode(em::DeviceStatusReportRequest* status) {
std::string dev_switch_mode;
bool anything_reported = false;
if (statistics_provider_->GetMachineStatistic(
chromeos::system::kDevSwitchBootKey, &dev_switch_mode)) {
if (dev_switch_mode == chromeos::system::kDevSwitchBootValueDev)
status->set_boot_mode("Dev");
else if (dev_switch_mode == chromeos::system::kDevSwitchBootValueVerified)
status->set_boot_mode("Verified");
anything_reported = true;
}
return anything_reported;
}
bool DeviceStatusCollector::GetWriteProtectSwitch(
em::DeviceStatusReportRequest* status) {
std::string firmware_write_protect;
if (!statistics_provider_->GetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey,
&firmware_write_protect)) {
return false;
}
if (firmware_write_protect ==
chromeos::system::kFirmwareWriteProtectBootValueOff) {
status->set_write_protect_switch(false);
} else if (firmware_write_protect ==
chromeos::system::kFirmwareWriteProtectBootValueOn) {
status->set_write_protect_switch(true);
} else {
return false;
}
return true;
}
bool DeviceStatusCollector::GetNetworkInterfaces(
em::DeviceStatusReportRequest* status) {
// Maps shill device type strings to proto enum constants.
static const struct {
const char* type_string;
em::NetworkInterface::NetworkDeviceType type_constant;
} kDeviceTypeMap[] = {
{ shill::kTypeEthernet, em::NetworkInterface::TYPE_ETHERNET, },
{ shill::kTypeWifi, em::NetworkInterface::TYPE_WIFI, },
{ shill::kTypeWimax, em::NetworkInterface::TYPE_WIMAX, },
{ shill::kTypeBluetooth, em::NetworkInterface::TYPE_BLUETOOTH, },
{ shill::kTypeCellular, em::NetworkInterface::TYPE_CELLULAR, },
};
// Maps shill device connection status to proto enum constants.
static const struct {
const char* state_string;
em::NetworkState::ConnectionState state_constant;
} kConnectionStateMap[] = {
{shill::kStateIdle, em::NetworkState::IDLE},
{shill::kStateCarrier, em::NetworkState::CARRIER},
{shill::kStateAssociation, em::NetworkState::ASSOCIATION},
{shill::kStateConfiguration, em::NetworkState::CONFIGURATION},
{shill::kStateReady, em::NetworkState::READY},
{shill::kStatePortal, em::NetworkState::PORTAL},
{shill::kStateOffline, em::NetworkState::OFFLINE},
{shill::kStateOnline, em::NetworkState::ONLINE},
{shill::kStateDisconnect, em::NetworkState::DISCONNECT},
{shill::kStateFailure, em::NetworkState::FAILURE},
{shill::kStateActivationFailure, em::NetworkState::ACTIVATION_FAILURE},
};
chromeos::NetworkStateHandler::DeviceStateList device_list;
chromeos::NetworkStateHandler* network_state_handler =
chromeos::NetworkHandler::Get()->network_state_handler();
network_state_handler->GetDeviceList(&device_list);
bool anything_reported = false;
chromeos::NetworkStateHandler::DeviceStateList::const_iterator device;
for (device = device_list.begin(); device != device_list.end(); ++device) {
// Determine the type enum constant for |device|.
size_t type_idx = 0;
for (; type_idx < base::size(kDeviceTypeMap); ++type_idx) {
if ((*device)->type() == kDeviceTypeMap[type_idx].type_string)
break;
}
// If the type isn't in |kDeviceTypeMap|, the interface is not relevant for
// reporting. This filters out VPN devices.
if (type_idx >= base::size(kDeviceTypeMap))
continue;
em::NetworkInterface* interface = status->add_network_interfaces();
interface->set_type(kDeviceTypeMap[type_idx].type_constant);
if (!(*device)->mac_address().empty())
interface->set_mac_address((*device)->mac_address());
if (!(*device)->meid().empty())
interface->set_meid((*device)->meid());
if (!(*device)->imei().empty())
interface->set_imei((*device)->imei());
if (!(*device)->path().empty())
interface->set_device_path((*device)->path());
anything_reported = true;
}
// Don't write any network state if we aren't in a kiosk or public session.
if (!GetAutoLaunchedKioskSessionInfo() &&
!user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) {
return anything_reported;
}
// Walk the various networks and store their state in the status report.
chromeos::NetworkStateHandler::NetworkStateList state_list;
network_state_handler->GetNetworkListByType(
chromeos::NetworkTypePattern::Default(),
true, // configured_only
false, // visible_only
0, // no limit to number of results
&state_list);
for (const chromeos::NetworkState* state : state_list) {
// Determine the connection state and signal strength for |state|.
em::NetworkState::ConnectionState connection_state_enum =
em::NetworkState::UNKNOWN;
const std::string connection_state_string(state->connection_state());
for (size_t i = 0; i < base::size(kConnectionStateMap); ++i) {
if (connection_state_string == kConnectionStateMap[i].state_string) {
connection_state_enum = kConnectionStateMap[i].state_constant;
break;
}
}
// Copy fields from NetworkState into the status report.
em::NetworkState* proto_state = status->add_network_states();
proto_state->set_connection_state(connection_state_enum);
anything_reported = true;
// Report signal strength for wifi connections.
if (state->type() == shill::kTypeWifi) {
// If shill has provided a signal strength, convert it to dBm and store it
// in the status report. A signal_strength() of 0 connotes "no signal"
// rather than "really weak signal", so we only report signal strength if
// it is non-zero.
if (state->signal_strength()) {
proto_state->set_signal_strength(
ConvertWifiSignalStrength(state->signal_strength()));
}
}
if (!state->device_path().empty())
proto_state->set_device_path(state->device_path());
std::string ip_address = state->GetIpAddress();
if (!ip_address.empty())
proto_state->set_ip_address(ip_address);
std::string gateway = state->GetGateway();
if (!gateway.empty())
proto_state->set_gateway(gateway);
}
return anything_reported;
}
bool DeviceStatusCollector::GetUsers(em::DeviceStatusReportRequest* status) {
const user_manager::UserList& users =
chromeos::ChromeUserManager::Get()->GetUsers();
bool anything_reported = false;
for (auto* user : users) {
// Only users with gaia accounts (regular) are reported.
if (!user->HasGaiaAccount())
continue;
em::DeviceUser* device_user = status->add_users();
if (chromeos::ChromeUserManager::Get()->ShouldReportUser(
user->GetAccountId().GetUserEmail())) {
device_user->set_type(em::DeviceUser::USER_TYPE_MANAGED);
device_user->set_email(user->GetAccountId().GetUserEmail());
} else {
device_user->set_type(em::DeviceUser::USER_TYPE_UNMANAGED);
// Do not report the email address of unmanaged users.
}
anything_reported = true;
}
return anything_reported;
}
bool DeviceStatusCollector::GetHardwareStatus(
scoped_refptr<DeviceStatusCollectorState> state) {
em::DeviceStatusReportRequest* status =
state->response_params().device_status.get();
// Sample disk volume info in a background thread.
state->SampleVolumeInfo(volume_info_fetcher_);
// Add CPU utilization and free RAM. Note that these stats are sampled in
// regular intervals. Unlike CPU temp and volume info these are not one-time
// sampled values, hence the difference in logic.
status->set_system_ram_total(base::SysInfo::AmountOfPhysicalMemory());
status->clear_system_ram_free_samples();
status->clear_cpu_utilization_pct_samples();
for (const ResourceUsage& usage : resource_usage_) {
status->add_cpu_utilization_pct_samples(usage.cpu_usage_percent);
status->add_system_ram_free_samples(usage.bytes_of_ram_free);
}
// Get the current device sound volume level.
chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
status->set_sound_volume(audio_handler->GetOutputVolumePercent());
// Fetch TPM status information on a background thread.
state->FetchTpmStatus(tpm_status_fetcher_);
// clear
status->clear_cpu_temp_infos();
if (report_power_status_ || report_storage_status_) {
state->FetchProbeData(probe_data_fetcher_);
} else {
// Sample CPU temperature in a background thread.
state->SampleCPUTempInfo(cpu_temp_fetcher_);
}
return true;
}
bool DeviceStatusCollector::GetOsUpdateStatus(
em::DeviceStatusReportRequest* status) {
const base::Version platform_version(GetPlatformVersion());
if (!platform_version.IsValid())
return false;
const std::string required_platform_version_string =
chromeos::KioskAppManager::Get()
->GetAutoLaunchAppRequiredPlatformVersion();
if (required_platform_version_string.empty())
return false;
const base::Version required_platfrom_version(
required_platform_version_string);
em::OsUpdateStatus* os_update_status = status->mutable_os_update_status();
os_update_status->set_new_required_platform_version(
required_platfrom_version.GetString());
if (platform_version == required_platfrom_version) {
os_update_status->set_update_status(em::OsUpdateStatus::OS_UP_TO_DATE);
return true;
}
const chromeos::UpdateEngineClient::Status update_engine_status =
chromeos::DBusThreadManager::Get()
->GetUpdateEngineClient()
->GetLastStatus();
if (update_engine_status.status ==
chromeos::UpdateEngineClient::UPDATE_STATUS_DOWNLOADING ||
update_engine_status.status ==
chromeos::UpdateEngineClient::UPDATE_STATUS_VERIFYING ||
update_engine_status.status ==
chromeos::UpdateEngineClient::UPDATE_STATUS_FINALIZING) {
os_update_status->set_update_status(
em::OsUpdateStatus::OS_IMAGE_DOWNLOAD_IN_PROGRESS);
os_update_status->set_new_platform_version(
update_engine_status.new_version);
} else if (update_engine_status.status ==
chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
os_update_status->set_update_status(
em::OsUpdateStatus::OS_UPDATE_NEED_REBOOT);
// Note the new_version could be a dummy "0.0.0.0" for some edge cases,
// e.g. update engine is somehow restarted without a reboot.
os_update_status->set_new_platform_version(
update_engine_status.new_version);
} else {
os_update_status->set_update_status(
em::OsUpdateStatus::OS_IMAGE_DOWNLOAD_NOT_STARTED);
}
return true;
}
bool DeviceStatusCollector::GetRunningKioskApp(
em::DeviceStatusReportRequest* status) {
// Must be on creation thread since some stats are written to in that thread
// and accessing them from another thread would lead to race conditions.
DCHECK(thread_checker_.CalledOnValidThread());
std::unique_ptr<const DeviceLocalAccount> account =
GetAutoLaunchedKioskSessionInfo();
// Only generate running kiosk app reports if we are in an auto-launched kiosk
// session.
if (!account)
return false;
em::AppStatus* running_kiosk_app = status->mutable_running_kiosk_app();
if (account->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP) {
running_kiosk_app->set_app_id(account->kiosk_app_id);
const std::string app_version = GetAppVersion(account->kiosk_app_id);
if (app_version.empty()) {
DLOG(ERROR) << "Unable to get version for extension: "
<< account->kiosk_app_id;
} else {
running_kiosk_app->set_extension_version(app_version);
}
chromeos::KioskAppManager::App app_info;
if (chromeos::KioskAppManager::Get()->GetApp(account->kiosk_app_id,
&app_info)) {
running_kiosk_app->set_required_platform_version(
app_info.required_platform_version);
}
} else if (account->type == policy::DeviceLocalAccount::TYPE_ARC_KIOSK_APP) {
// Use package name as app ID for ARC Kiosks.
running_kiosk_app->set_app_id(account->arc_kiosk_app_info.package_name());
} else {
NOTREACHED();
}
return true;
}
void DeviceStatusCollector::GetStatusAsync(
const StatusCollectorCallback& response) {
// Must be on creation thread since some stats are written to in that thread
// and accessing them from another thread would lead to race conditions.
DCHECK(thread_checker_.CalledOnValidThread());
// Some of the data we're collecting is gathered in background threads.
// This object keeps track of the state of each async request.
scoped_refptr<DeviceStatusCollectorState> state(
new DeviceStatusCollectorState(task_runner_, response));
// Gather device status (might queue some async queries)
GetDeviceStatus(state);
// Gather session status (might queue some async queries)
GetSessionStatus(state);
// If there are no outstanding async queries, e.g. from GetHardwareStatus(),
// the destructor of |state| calls |response|. If there are async queries, the
// queries hold references to |state|, so that |state| is only destroyed when
// the last async query has finished.
}
void DeviceStatusCollector::GetDeviceStatus(
scoped_refptr<DeviceStatusCollectorState> state) {
em::DeviceStatusReportRequest* status =
state->response_params().device_status.get();
bool anything_reported = false;
if (report_activity_times_)
anything_reported |= GetActivityTimes(status);
if (report_version_info_)
anything_reported |= GetVersionInfo(status);
if (report_boot_mode_)
anything_reported |= GetBootMode(status);
if (report_network_interfaces_)
anything_reported |= GetNetworkInterfaces(status);
if (report_users_)
anything_reported |= GetUsers(status);
if (report_hardware_status_) {
anything_reported |= GetHardwareStatus(state);
anything_reported |= GetWriteProtectSwitch(status);
}
if (report_os_update_status_)
anything_reported |= GetOsUpdateStatus(status);
if (report_running_kiosk_app_)
anything_reported |= GetRunningKioskApp(status);
// Wipe pointer if we didn't actually add any data.
if (!anything_reported)
state->response_params().device_status.reset();
}
std::string DeviceStatusCollector::GetDMTokenForProfile(Profile* profile) {
CloudPolicyManager* user_cloud_policy_manager =
UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
profile);
if (!user_cloud_policy_manager) {
NOTREACHED();
return std::string();
}
auto* cloud_policy_client = user_cloud_policy_manager->core()->client();
std::string dm_token = cloud_policy_client->dm_token();
return dm_token;
}
bool DeviceStatusCollector::GetSessionStatusForUser(
scoped_refptr<DeviceStatusCollectorState> state,
em::SessionStatusReportRequest* status,
const user_manager::User* user) {
Profile* const profile =
chromeos::ProfileHelper::Get()->GetProfileByUser(user);
if (!profile)
return false;
bool anything_reported_user = false;
const bool report_android_status =
profile->GetPrefs()->GetBoolean(prefs::kReportArcStatusEnabled);
if (report_android_status)
anything_reported_user |= GetAndroidStatus(status, state);
const bool report_crostini_usage = profile->GetPrefs()->GetBoolean(
crostini::prefs::kReportCrostiniUsageEnabled);
if (report_crostini_usage)
anything_reported_user |= GetCrostiniUsage(status, profile);
if (anything_reported_user && !user->IsDeviceLocalAccount())
status->set_user_dm_token(GetDMTokenForProfile(profile));
// Time zone is not reported in enterprise reports.
if (!is_enterprise_reporting_) {
const std::string current_timezone =
base::UTF16ToUTF8(chromeos::system::TimezoneSettings::GetInstance()
->GetCurrentTimezoneID());
status->set_time_zone(current_timezone);
anything_reported_user = true;
}
return anything_reported_user;
}
void DeviceStatusCollector::GetSessionStatus(
scoped_refptr<DeviceStatusCollectorState> state) {
em::SessionStatusReportRequest* status =
state->response_params().session_status.get();
bool anything_reported = false;
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
const user_manager::User* const primary_user = user_manager->GetPrimaryUser();
if (report_kiosk_session_status_)
anything_reported |= GetKioskSessionStatus(status);
// Only report affiliated users' data in enterprise reporting and registered
// user data in consumer reporting. Note that device-local accounts are also
// affiliated. Currently we only report for the primary user.
if (primary_user &&
(!is_enterprise_reporting_ || primary_user->IsAffiliated())) {
anything_reported |= GetSessionStatusForUser(state, status, primary_user);
}
// Wipe pointer if we didn't actually add any data.
if (!anything_reported)
state->response_params().session_status.reset();
}
bool DeviceStatusCollector::GetKioskSessionStatus(
em::SessionStatusReportRequest* status) {
std::unique_ptr<const DeviceLocalAccount> account =
GetAutoLaunchedKioskSessionInfo();
if (!account)
return false;
// Get the account ID associated with this user.
status->set_device_local_account_id(account->account_id);
em::AppStatus* app_status = status->add_installed_apps();
if (account->type == policy::DeviceLocalAccount::TYPE_KIOSK_APP) {
app_status->set_app_id(account->kiosk_app_id);
// Look up the app and get the version.
const std::string app_version = GetAppVersion(account->kiosk_app_id);
if (app_version.empty()) {
DLOG(ERROR) << "Unable to get version for extension: "
<< account->kiosk_app_id;
} else {
app_status->set_extension_version(app_version);
}
} else if (account->type == policy::DeviceLocalAccount::TYPE_ARC_KIOSK_APP) {
// Use package name as app ID for ARC Kiosks.
app_status->set_app_id(account->arc_kiosk_app_info.package_name());
} else {
NOTREACHED();
}
return true;
}
bool DeviceStatusCollector::GetAndroidStatus(
em::SessionStatusReportRequest* status,
const scoped_refptr<DeviceStatusCollectorState>& state) {
return state->FetchAndroidStatus(android_status_fetcher_);
}
bool DeviceStatusCollector::GetCrostiniUsage(
em::SessionStatusReportRequest* status,
Profile* profile) {
if (!profile->GetPrefs()->HasPrefPath(
crostini::prefs::kCrostiniLastLaunchTimeWindowStart)) {
return false;
}
em::CrostiniStatus* const crostini_status = status->mutable_crostini_status();
const int64_t last_launch_time_window_start = profile->GetPrefs()->GetInt64(
crostini::prefs::kCrostiniLastLaunchTimeWindowStart);
const std::string& termina_version = profile->GetPrefs()->GetString(
crostini::prefs::kCrostiniLastLaunchVersion);
crostini_status->set_last_launch_time_window_start_timestamp(
last_launch_time_window_start);
crostini_status->set_last_launch_vm_image_version(termina_version);
return true;
}
std::string DeviceStatusCollector::GetAppVersion(
const std::string& kiosk_app_id) {
Profile* const profile = chromeos::ProfileHelper::Get()->GetProfileByUser(
user_manager::UserManager::Get()->GetActiveUser());
const extensions::ExtensionRegistry* const registry =
extensions::ExtensionRegistry::Get(profile);
const extensions::Extension* const extension = registry->GetExtensionById(
kiosk_app_id, extensions::ExtensionRegistry::EVERYTHING);
if (!extension)
return std::string();
return extension->VersionString();
}
// TODO(crbug.com/827386): move public API methods above private ones after
// common methods are extracted.
void DeviceStatusCollector::OnSubmittedSuccessfully() {
activity_storage_->TrimActivityPeriods(last_reported_day_,
duration_for_last_reported_day_,
std::numeric_limits<int64_t>::max());
}
bool DeviceStatusCollector::ShouldReportActivityTimes() const {
return report_activity_times_;
}
bool DeviceStatusCollector::ShouldReportNetworkInterfaces() const {
return report_network_interfaces_;
}
bool DeviceStatusCollector::ShouldReportUsers() const {
return report_users_;
}
bool DeviceStatusCollector::ShouldReportHardwareStatus() const {
return report_hardware_status_;
}
void DeviceStatusCollector::OnOSVersion(const std::string& version) {
os_version_ = version;
}
void DeviceStatusCollector::OnOSFirmware(const std::string& version) {
firmware_version_ = version;
}
void DeviceStatusCollector::OnTpmVersion(
const chromeos::CryptohomeClient::TpmVersionInfo& tpm_version_info) {
tpm_version_info_ = tpm_version_info;
}
} // namespace policy