blob: 26105e9c1f595fe8b21228167c484630f1cdf1f2 [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/device_status_collector.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/environment.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_path_override.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_data.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/mock_user_manager.h"
#include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/chromeos/settings/stub_install_attributes.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_unit_test_suite.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/dbus/cros_disks_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_power_manager_client.h"
#include "chromeos/dbus/fake_update_engine_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "chromeos/dbus/shill_device_client.h"
#include "chromeos/dbus/shill_ipconfig_client.h"
#include "chromeos/dbus/shill_profile_client.h"
#include "chromeos/dbus/shill_service_client.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "chromeos/disks/mock_disk_mount_manager.h"
#include "chromeos/login/login_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/fake_statistics_provider.h"
#include "components/account_id/account_id.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_type.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "storage/browser/fileapi/external_mount_points.h"
#include "storage/browser/fileapi/mount_points.h"
#include "storage/common/fileapi/file_system_mount_option.h"
#include "storage/common/fileapi/file_system_types.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using ::testing::Return;
using ::testing::ReturnRef;
using base::Time;
using base::TimeDelta;
using chromeos::disks::DiskMountManager;
namespace em = enterprise_management;
namespace {
// Time delta representing midnight 00:00.
constexpr TimeDelta kMidnight;
// Time delta representing 06:00AM.
constexpr TimeDelta kSixAm = TimeDelta::FromHours(6);
// Time delta representing 1 hour time interval.
constexpr TimeDelta kHour = TimeDelta::FromHours(1);
const int64_t kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000;
const char kKioskAccountId[] = "kiosk_user@localhost";
const char kArcKioskAccountId[] = "arc_kiosk_user@localhost";
const char kKioskAppId[] = "kiosk_app_id";
const char kArcKioskPackageName[] = "com.test.kioskapp";
const char kExternalMountPoint[] = "/a/b/c";
const char kPublicAccountId[] = "public_user@localhost";
const char kArcStatus[] = R"(
{
"applications":[
{
"packageName":"com.android.providers.telephony",
"versionName":"6.0.1",
"permissions":[ "android.permission.INTERNET" ]
}
],
"userEmail":"xxx@google.com"
})";
const char kDroidGuardInfo[] = "{\"droid_guard_info\":42}";
const char kShillFakeProfilePath[] = "/profile/user1/shill";
const char kShillFakeUserhash[] = "user1";
class TestingDeviceStatusCollector : public policy::DeviceStatusCollector {
public:
TestingDeviceStatusCollector(
PrefService* pref_service,
chromeos::system::StatisticsProvider* provider,
const policy::DeviceStatusCollector::VolumeInfoFetcher&
volume_info_fetcher,
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_fetcher,
const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher,
const policy::DeviceStatusCollector::AndroidStatusFetcher&
android_status_fetcher,
const policy::DeviceStatusCollector::TpmStatusFetcher& tpm_status_fetcher,
TimeDelta activity_day_start,
bool is_enterprise_device)
: policy::DeviceStatusCollector(pref_service,
provider,
volume_info_fetcher,
cpu_fetcher,
cpu_temp_fetcher,
android_status_fetcher,
tpm_status_fetcher,
activity_day_start,
is_enterprise_device) {
// Set the baseline time to a fixed value (1 hour after day start) to
// prevent test flakiness due to a single activity period spanning two days.
SetBaselineTime(Time::Now().LocalMidnight() + activity_day_start + kHour);
}
void UpdateUsageTime() { UpdateChildUsageTime(); }
void Simulate(ui::IdleState* states, int len) {
for (int i = 0; i < len; i++)
ProcessIdleState(states[i]);
}
void set_max_stored_past_activity_interval(TimeDelta value) {
max_stored_past_activity_interval_ = value;
}
void set_max_stored_future_activity_interval(TimeDelta value) {
max_stored_future_activity_interval_ = value;
}
// Reset the baseline time.
void SetBaselineTime(Time time) {
baseline_time_ = time;
baseline_offset_periods_ = 0;
}
void set_kiosk_account(std::unique_ptr<policy::DeviceLocalAccount> account) {
kiosk_account_ = std::move(account);
}
std::unique_ptr<policy::DeviceLocalAccount> GetAutoLaunchedKioskSessionInfo()
override {
if (kiosk_account_)
return std::make_unique<policy::DeviceLocalAccount>(*kiosk_account_);
return std::unique_ptr<policy::DeviceLocalAccount>();
}
std::string GetAppVersion(const std::string& app_id) override {
// Just return the app_id as the version - this makes it easy for tests
// to confirm that the correct app's version was requested.
return app_id;
}
std::string GetDMTokenForProfile(Profile* profile) override {
// Return the profile user name (passed to CreateTestingProfile) to make it
// easy to confirm that the correct profile's DMToken was requested.
return profile->GetProfileUserName();
}
void RefreshSampleResourceUsage() {
SampleResourceUsage();
content::RunAllTasksUntilIdle();
}
protected:
void CheckIdleState() override {
// This should never be called in testing, as it results in a dbus call.
ADD_FAILURE();
}
// Each time this is called, returns a time that is a fixed increment
// later than the previous time.
Time GetCurrentTime() override {
int poll_interval = policy::DeviceStatusCollector::kIdlePollIntervalSeconds;
return baseline_time_ +
TimeDelta::FromSeconds(poll_interval * baseline_offset_periods_++);
}
private:
// Baseline time for the fake times returned from GetCurrentTime().
Time baseline_time_;
// The number of simulated periods since the baseline time.
int baseline_offset_periods_;
std::unique_ptr<policy::DeviceLocalAccount> kiosk_account_;
};
// Return the total number of active milliseconds contained in a device
// status report.
int64_t GetActiveMilliseconds(const em::DeviceStatusReportRequest& status) {
int64_t active_milliseconds = 0;
for (int i = 0; i < status.active_period_size(); i++) {
active_milliseconds += status.active_period(i).active_duration();
}
return active_milliseconds;
}
// Mock CPUStatisticsFetcher used to return an empty set of statistics.
std::string GetEmptyCPUStatistics() {
return std::string();
}
std::string GetFakeCPUStatistics(const std::string& fake) {
return fake;
}
// Mock VolumeInfoFetcher used to return empty VolumeInfo, to avoid warnings
// and test slowdowns from trying to fetch information about non-existent
// volumes.
std::vector<em::VolumeInfo> GetEmptyVolumeInfo(
const std::vector<std::string>& mount_points) {
return std::vector<em::VolumeInfo>();
}
std::vector<em::VolumeInfo> GetFakeVolumeInfo(
const std::vector<em::VolumeInfo>& volume_info,
const std::vector<std::string>& mount_points) {
EXPECT_EQ(volume_info.size(), mount_points.size());
// Make sure there's a matching mount point for every volume info.
for (const em::VolumeInfo& info : volume_info) {
bool found = false;
for (const std::string& mount_point : mount_points) {
if (info.volume_id() == mount_point) {
found = true;
break;
}
}
EXPECT_TRUE(found) << "Could not find matching mount point for "
<< info.volume_id();
}
return volume_info;
}
std::vector<em::CPUTempInfo> GetEmptyCPUTempInfo() {
return std::vector<em::CPUTempInfo>();
}
std::vector<em::CPUTempInfo> GetFakeCPUTempInfo(
const std::vector<em::CPUTempInfo>& cpu_temp_info) {
return cpu_temp_info;
}
void CallAndroidStatusReceiver(
const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver,
const std::string& status,
const std::string& droid_guard_info) {
receiver.Run(status, droid_guard_info);
}
bool GetEmptyAndroidStatus(
const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
// Post it to the thread because this call is expected to be asynchronous.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, "", ""));
return true;
}
bool GetFakeAndroidStatus(
const std::string& status,
const std::string& droid_guard_info,
const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
// Post it to the thread because this call is expected to be asynchronous.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, status,
droid_guard_info));
return true;
}
void GetEmptyTpmStatus(
policy::DeviceStatusCollector::TpmStatusReceiver receiver) {
std::move(receiver).Run(policy::TpmStatusInfo());
}
void GetFakeTpmStatus(
const policy::TpmStatusInfo& tpm_status_info,
policy::DeviceStatusCollector::TpmStatusReceiver receiver) {
std::move(receiver).Run(tpm_status_info);
}
} // namespace
namespace policy {
// Though it is a unit test, this test is linked with browser_tests so that it
// runs in a separate process. The intention is to avoid overriding the timezone
// environment variable for other tests.
class DeviceStatusCollectorTest : public testing::Test {
public:
DeviceStatusCollectorTest()
: user_manager_(new chromeos::MockUserManager()),
user_manager_enabler_(base::WrapUnique(user_manager_)),
got_session_status_(false),
fake_kiosk_device_local_account_(
policy::DeviceLocalAccount::TYPE_KIOSK_APP,
kKioskAccountId,
kKioskAppId,
std::string() /* kiosk_app_update_url */),
fake_arc_kiosk_app_basic_info_(kArcKioskPackageName,
std::string() /* class_name */,
std::string() /* action */,
std::string() /* display_name */),
fake_arc_kiosk_device_local_account_(fake_arc_kiosk_app_basic_info_,
kArcKioskAccountId),
user_data_dir_override_(chrome::DIR_USER_DATA),
update_engine_client_(new chromeos::FakeUpdateEngineClient) {
settings_helper_.InstallAttributes()->SetCloudManaged("managed.com",
"device_id");
EXPECT_CALL(*user_manager_, Shutdown()).Times(1);
// Although this is really a unit test which runs in the browser_tests
// binary, it doesn't get the unit setup which normally happens in the unit
// test binary.
ChromeUnitTestSuite::InitializeProviders();
ChromeUnitTestSuite::InitializeResourceBundle();
content::SetContentClient(&content_client_);
content::SetBrowserClientForTesting(&browser_content_client_);
// Run this test with a well-known timezone so that Time::LocalMidnight()
// returns the same values on all machines.
std::unique_ptr<base::Environment> env(base::Environment::Create());
env->SetVar("TZ", "UTC");
// Initialize our mock mounted disk volumes.
std::unique_ptr<chromeos::disks::MockDiskMountManager>
mock_disk_mount_manager =
std::make_unique<chromeos::disks::MockDiskMountManager>();
AddMountPoint("/mount/volume1");
AddMountPoint("/mount/volume2");
EXPECT_CALL(*mock_disk_mount_manager, mount_points())
.WillRepeatedly(ReturnRef(mount_point_map_));
// Setup a fake file system that should show up in mount points.
storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
"c", storage::kFileSystemTypeNativeLocal,
storage::FileSystemMountOption(), base::FilePath(kExternalMountPoint));
// Just verify that we are properly setting the mount points.
std::vector<storage::MountPoints::MountPointInfo> external_mount_points;
storage::ExternalMountPoints::GetSystemInstance()->AddMountPointInfosTo(
&external_mount_points);
EXPECT_FALSE(external_mount_points.empty());
// DiskMountManager takes ownership of the MockDiskMountManager.
DiskMountManager::InitializeForTesting(mock_disk_mount_manager.release());
TestingDeviceStatusCollector::RegisterPrefs(local_state_.registry());
TestingDeviceStatusCollector::RegisterProfilePrefs(
profile_pref_service_.registry());
settings_helper_.ReplaceDeviceSettingsProviderWithStub();
owner_settings_service_ =
settings_helper_.CreateOwnerSettingsService(nullptr);
owner_settings_service_->set_ignore_profile_creation_notification(true);
// Set up a fake local state for KioskAppManager.
TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
chromeos::KioskAppManager::RegisterPrefs(local_state_.registry());
// Use FakeUpdateEngineClient.
std::unique_ptr<chromeos::DBusThreadManagerSetter> dbus_setter =
chromeos::DBusThreadManager::GetSetterForTesting();
dbus_setter->SetUpdateEngineClient(
base::WrapUnique<chromeos::UpdateEngineClient>(update_engine_client_));
chromeos::CrasAudioHandler::InitializeForTesting();
chromeos::LoginState::Initialize();
fake_power_manager_client_ = new chromeos::FakePowerManagerClient;
chromeos::DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
base::WrapUnique(fake_power_manager_client_));
}
~DeviceStatusCollectorTest() override {
chromeos::LoginState::Shutdown();
chromeos::CrasAudioHandler::Shutdown();
chromeos::KioskAppManager::Shutdown();
TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
// Finish pending tasks.
content::RunAllTasksUntilIdle();
storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
DiskMountManager::Shutdown();
}
void SetUp() override {
RestartStatusCollector(base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
// Disable network interface reporting since it requires additional setup.
settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces,
false);
}
void TearDown() override {
settings_helper_.RestoreRealDeviceSettingsProvider();
}
protected:
// States tracked to calculate a child's active time.
enum class DeviceStateTransitions {
kEnterIdleState,
kLeaveIdleState,
kEnterSleep,
kLeaveSleep,
kEnterSessionActive,
kLeaveSessionActive,
kPeriodicCheckTriggered
};
void SimulateStateChanges(DeviceStateTransitions* states, int len) {
for (int i = 0; i < len; i++) {
switch (states[i]) {
case DeviceStateTransitions::kEnterIdleState: {
power_manager::ScreenIdleState state;
state.set_off(true);
fake_power_manager_client_->SendScreenIdleStateChanged(state);
} break;
case DeviceStateTransitions::kLeaveIdleState: {
power_manager::ScreenIdleState state;
state.set_off(false);
fake_power_manager_client_->SendScreenIdleStateChanged(state);
} break;
case DeviceStateTransitions::kEnterSleep:
fake_power_manager_client_->SendSuspendImminent(
power_manager::SuspendImminent_Reason_LID_CLOSED);
break;
case DeviceStateTransitions::kLeaveSleep:
fake_power_manager_client_->SendSuspendDone(
base::TimeDelta::FromSeconds(
policy::DeviceStatusCollector::kIdlePollIntervalSeconds));
break;
case DeviceStateTransitions::kEnterSessionActive:
session_manager::SessionManager::Get()->SetSessionState(
session_manager::SessionState::ACTIVE);
break;
case DeviceStateTransitions::kLeaveSessionActive:
session_manager::SessionManager::Get()->SetSessionState(
session_manager::SessionState::LOCKED);
break;
case DeviceStateTransitions::kPeriodicCheckTriggered:
status_collector_->UpdateUsageTime();
break;
}
}
}
void AddMountPoint(const std::string& mount_point) {
mount_point_map_.insert(DiskMountManager::MountPointMap::value_type(
mount_point, DiskMountManager::MountPointInfo(
mount_point, mount_point, chromeos::MOUNT_TYPE_DEVICE,
chromeos::disks::MOUNT_CONDITION_NONE)));
}
virtual void RestartStatusCollector(
const policy::DeviceStatusCollector::VolumeInfoFetcher& volume_info,
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_stats,
const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher,
const policy::DeviceStatusCollector::AndroidStatusFetcher&
android_status_fetcher,
const policy::DeviceStatusCollector::TpmStatusFetcher& tpm_status_fetcher,
const TimeDelta activity_day_start = kMidnight) {
std::vector<em::VolumeInfo> expected_volume_info;
status_collector_.reset(new TestingDeviceStatusCollector(
&local_state_, &fake_statistics_provider_, volume_info, cpu_stats,
cpu_temp_fetcher, android_status_fetcher, tpm_status_fetcher,
activity_day_start, true /* is_enterprise_device */));
}
void GetStatus() {
device_status_.Clear();
session_status_.Clear();
got_session_status_ = false;
run_loop_.reset(new base::RunLoop());
status_collector_->GetDeviceAndSessionStatusAsync(base::BindRepeating(
&DeviceStatusCollectorTest::OnStatusReceived, base::Unretained(this)));
run_loop_->Run();
run_loop_.reset();
}
void OnStatusReceived(
std::unique_ptr<em::DeviceStatusReportRequest> device_status,
std::unique_ptr<em::SessionStatusReportRequest> session_status) {
if (device_status)
device_status_ = *device_status;
got_session_status_ = session_status != nullptr;
if (got_session_status_)
session_status_ = *session_status;
EXPECT_TRUE(run_loop_);
run_loop_->Quit();
}
void MockUserWithTypeAndAffiliation(const AccountId& account_id,
user_manager::UserType user_type,
bool is_affiliated) {
user_manager_->AddUserWithAffiliationAndType(account_id, is_affiliated,
user_type);
// The user just added will be the active user because there's only one
// user.
user_manager::User* user = user_manager_->GetActiveUser();
// Build a profile with profile name=account e-mail because our testing
// version of GetDMTokenForProfile returns the profile name.
TestingProfile::Builder profile_builder;
profile_builder.SetProfileName(account_id.GetUserEmail());
testing_profile_ = profile_builder.Build();
chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
user, testing_profile_.get());
EXPECT_CALL(*user_manager_, IsLoggedInAsKioskApp())
.WillRepeatedly(Return(false));
}
void MockRegularUserWithAffiliation(const AccountId& account_id,
bool is_affiliated) {
MockUserWithTypeAndAffiliation(account_id, user_manager::USER_TYPE_REGULAR,
is_affiliated);
}
void MockChildUser(const AccountId& account_id) {
MockUserWithTypeAndAffiliation(account_id, user_manager::USER_TYPE_CHILD,
false);
EXPECT_CALL(*user_manager_, IsLoggedInAsChildUser())
.WillRepeatedly(Return(true));
}
void MockRunningKioskApp(const DeviceLocalAccount& account, bool arc_kiosk) {
std::vector<DeviceLocalAccount> accounts;
accounts.push_back(account);
user_manager::User* user = user_manager_->CreateKioskAppUser(
AccountId::FromUserEmail(account.user_id));
if (arc_kiosk) {
EXPECT_CALL(*user_manager_, IsLoggedInAsArcKioskApp())
.WillRepeatedly(Return(true));
} else {
EXPECT_CALL(*user_manager_, IsLoggedInAsKioskApp())
.WillRepeatedly(Return(true));
}
testing_profile_ = std::make_unique<TestingProfile>();
chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
user, testing_profile_.get());
SetDeviceLocalAccounts(owner_settings_service_.get(), accounts);
}
void MockPlatformVersion(const std::string& platform_version) {
const std::string lsb_release = base::StringPrintf(
"CHROMEOS_RELEASE_VERSION=%s", platform_version.c_str());
base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release,
base::Time::Now());
}
void MockAutoLaunchKioskAppWithRequiredPlatformVersion(
const DeviceLocalAccount& auto_launch_app_account,
const std::string& required_platform_version) {
chromeos::KioskAppManager* manager = chromeos::KioskAppManager::Get();
manager->AddAppForTest(
auto_launch_app_account.kiosk_app_id,
AccountId::FromUserEmail(auto_launch_app_account.user_id),
GURL("http://cws/"), // Dummy URL to avoid setup ExtensionsClient.
required_platform_version);
manager->SetEnableAutoLaunch(true);
std::vector<DeviceLocalAccount> accounts;
accounts.push_back(auto_launch_app_account);
SetDeviceLocalAccounts(owner_settings_service_.get(), accounts);
owner_settings_service_->SetString(
chromeos::kAccountsPrefDeviceLocalAccountAutoLoginId,
auto_launch_app_account.account_id);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(required_platform_version,
manager->GetAutoLaunchAppRequiredPlatformVersion());
}
void MockAutoLaunchArcKioskApp(
const DeviceLocalAccount& auto_launch_app_account) {
arc_kiosk_app_manager_.reset(new chromeos::ArcKioskAppManager());
arc_kiosk_app_manager_->AddAutoLaunchAppForTest(
auto_launch_app_account.arc_kiosk_app_info.package_name(),
auto_launch_app_account.arc_kiosk_app_info,
AccountId::FromUserEmail(auto_launch_app_account.user_id));
std::vector<DeviceLocalAccount> accounts;
accounts.push_back(auto_launch_app_account);
SetDeviceLocalAccounts(owner_settings_service_.get(), accounts);
owner_settings_service_->SetString(
chromeos::kAccountsPrefDeviceLocalAccountAutoLoginId,
auto_launch_app_account.account_id);
base::RunLoop().RunUntilIdle();
}
// Convenience method.
int64_t ActivePeriodMilliseconds() {
return policy::DeviceStatusCollector::kIdlePollIntervalSeconds * 1000;
}
// Since this is a unit test running in browser_tests we must do additional
// unit test setup and make a TestingBrowserProcess. Must be first member.
TestingBrowserProcessInitializer initializer_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
ChromeContentClient content_client_;
ChromeContentBrowserClient browser_content_client_;
chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
DiskMountManager::MountPointMap mount_point_map_;
chromeos::ScopedCrosSettingsTestHelper settings_helper_;
// Only set after MockRunningKioskApp or MockTODO was called.
std::unique_ptr<chromeos::FakeOwnerSettingsService> owner_settings_service_;
// Only set after MockRunningKioskApp was called.
std::unique_ptr<TestingProfile> testing_profile_;
// Only set after MockAutoLaunchArcKioskApp was called.
std::unique_ptr<chromeos::ArcKioskAppManager> arc_kiosk_app_manager_;
chromeos::MockUserManager* const user_manager_;
user_manager::ScopedUserManager user_manager_enabler_;
em::DeviceStatusReportRequest device_status_;
em::SessionStatusReportRequest session_status_;
bool got_session_status_;
TestingPrefServiceSimple local_state_;
TestingPrefServiceSimple profile_pref_service_;
std::unique_ptr<TestingDeviceStatusCollector> status_collector_;
const policy::DeviceLocalAccount fake_kiosk_device_local_account_;
const policy::ArcKioskAppBasicInfo fake_arc_kiosk_app_basic_info_;
const policy::DeviceLocalAccount fake_arc_kiosk_device_local_account_;
base::ScopedPathOverride user_data_dir_override_;
chromeos::FakeUpdateEngineClient* const update_engine_client_;
std::unique_ptr<base::RunLoop> run_loop_;
// Owned by chromeos::DBusThreadManager.
chromeos::FakePowerManagerClient* fake_power_manager_client_;
// This property is required to instantiate the session manager, a singleton
// which is used by the device status collector.
session_manager::SessionManager session_manager_;
};
TEST_F(DeviceStatusCollectorTest, AllIdle) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_IDLE
};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
// Test reporting with no data.
GetStatus();
EXPECT_EQ(0, device_status_.active_period_size());
EXPECT_EQ(0, GetActiveMilliseconds(device_status_));
// Test reporting with a single idle sample.
status_collector_->Simulate(test_states, 1);
GetStatus();
EXPECT_EQ(0, device_status_.active_period_size());
EXPECT_EQ(0, GetActiveMilliseconds(device_status_));
// Test reporting with multiple consecutive idle samples.
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(0, device_status_.active_period_size());
EXPECT_EQ(0, GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, AllActive) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE
};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
// Test a single active sample.
status_collector_->Simulate(test_states, 1);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
device_status_.clear_active_period(); // Clear the result protobuf.
// Test multiple consecutive active samples.
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, MixedStates) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE
};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
// For kiosks report total uptime instead of only active periods.
TEST_F(DeviceStatusCollectorTest, MixedStatesForKiosk) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_IDLE,
};
chromeos::LoginState::Get()->SetLoggedInState(
chromeos::LoginState::LOGGED_IN_ACTIVE,
chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP);
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(6 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
// For Arc kiosks report total uptime instead of only active periods.
TEST_F(DeviceStatusCollectorTest, MixedStatesForArcKiosk) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
};
chromeos::LoginState::Get()->SetLoggedInState(
chromeos::LoginState::LOGGED_IN_ACTIVE,
chromeos::LoginState::LOGGED_IN_USER_ARC_KIOSK_APP);
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(5 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, StateKeptInPref) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_IDLE
};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
// Process the list a second time after restarting the collector. It should be
// able to count the active periods found by the original collector, because
// the results are stored in a pref.
RestartStatusCollector(base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(6 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, ActivityNotWrittenToProfilePref) {
EXPECT_TRUE(
profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
// Nothing should be written to profile pref service, because it is only used
// for consumer reporting.
EXPECT_TRUE(
profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
}
TEST_F(DeviceStatusCollectorTest, MaxStoredPeriods) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_IDLE
};
const int kMaxDays = 10;
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->set_max_stored_past_activity_interval(
TimeDelta::FromDays(kMaxDays - 1));
status_collector_->set_max_stored_future_activity_interval(
TimeDelta::FromDays(1));
Time baseline = Time::Now().LocalMidnight();
// Simulate 12 active periods.
for (int i = 0; i < kMaxDays + 2; i++) {
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
// Advance the simulated clock by a day.
baseline += TimeDelta::FromDays(1);
status_collector_->SetBaselineTime(baseline);
}
// Check that we don't exceed the max number of periods.
GetStatus();
EXPECT_EQ(kMaxDays - 1, device_status_.active_period_size());
// Simulate some future times.
for (int i = 0; i < kMaxDays + 2; i++) {
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
// Advance the simulated clock by a day.
baseline += TimeDelta::FromDays(1);
status_collector_->SetBaselineTime(baseline);
}
// Set the clock back so the previous simulated times are in the future.
baseline -= TimeDelta::FromDays(20);
status_collector_->SetBaselineTime(baseline);
// Collect one more data point to trigger pruning.
status_collector_->Simulate(test_states, 1);
// Check that we don't exceed the max number of periods.
device_status_.clear_active_period();
GetStatus();
EXPECT_LT(device_status_.active_period_size(), kMaxDays);
}
TEST_F(DeviceStatusCollectorTest, ActivityTimesEnabledByDefault) {
// Device activity times should be reported by default.
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE
};
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, ActivityTimesOff) {
// Device activity times should not be reported if explicitly disabled.
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, false);
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE
};
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));
GetStatus();
EXPECT_EQ(0, device_status_.active_period_size());
EXPECT_EQ(0, GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, ActivityCrossingMidnight) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE
};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
// Set the baseline time to 10 seconds after midnight.
status_collector_->SetBaselineTime(
Time::Now().LocalMidnight() + TimeDelta::FromSeconds(10));
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
em::ActiveTimePeriod period0 = device_status_.active_period(0);
em::ActiveTimePeriod period1 = device_status_.active_period(1);
EXPECT_EQ(ActivePeriodMilliseconds() - 10000, period0.active_duration());
EXPECT_EQ(10000, period1.active_duration());
em::TimePeriod time_period0 = period0.time_period();
em::TimePeriod time_period1 = period1.time_period();
EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp());
// Ensure that the start and end times for the period are a day apart.
EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(),
kMillisecondsPerDay);
EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(),
kMillisecondsPerDay);
}
TEST_F(DeviceStatusCollectorTest, ActivityTimesKeptUntilSubmittedSuccessfully) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
};
// Make sure CPU stats get reported in time. If we don't run this, the second
// call to |GetStatus()| will contain these stats, but the first call won't
// and the EXPECT_EQ test below fails.
base::RunLoop().RunUntilIdle();
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
status_collector_->Simulate(test_states, 2);
GetStatus();
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
em::DeviceStatusReportRequest first_status(device_status_);
// The collector returns the same activity times again.
GetStatus();
int period_count = first_status.active_period_size();
EXPECT_EQ(period_count, device_status_.active_period_size());
for (int n = 0; n < period_count; ++n) {
EXPECT_EQ(first_status.active_period(n).SerializeAsString(),
device_status_.active_period(n).SerializeAsString());
}
// After indicating a successful submit, the submitted status gets cleared,
// but what got collected meanwhile sticks around.
status_collector_->Simulate(test_states, 1);
status_collector_->OnSubmittedSuccessfully();
GetStatus();
EXPECT_EQ(ActivePeriodMilliseconds(), GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorTest, ActivityNoUser) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_TRUE(device_status_.active_period(0).user_email().empty());
}
TEST_F(DeviceStatusCollectorTest, ActivityWithPublicSessionUser) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
const AccountId public_account_id(
AccountId::FromUserEmail("public@localhost"));
user_manager_->CreatePublicAccountUser(public_account_id);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_TRUE(device_status_.active_period(0).user_email().empty());
}
TEST_F(DeviceStatusCollectorTest, ActivityWithAffiliatedUser) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
const AccountId account_id0(AccountId::FromUserEmail("user0@managed.com"));
user_manager_->AddUserWithAffiliationAndType(account_id0, true,
user_manager::USER_TYPE_REGULAR);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(account_id0.GetUserEmail(),
device_status_.active_period(0).user_email());
device_status_.clear_active_period(); // Clear the result protobuf.
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, false);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_TRUE(device_status_.active_period(0).user_email().empty());
}
TEST_F(DeviceStatusCollectorTest, ActivityWithNotAffiliatedUser) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
const AccountId account_id0(AccountId::FromUserEmail("user0@managed.com"));
user_manager_->AddUserWithAffiliationAndType(account_id0, false,
user_manager::USER_TYPE_REGULAR);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_TRUE(device_status_.active_period(0).user_email().empty());
device_status_.clear_active_period(); // Clear the result protobuf.
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, false);
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_TRUE(device_status_.active_period(0).user_email().empty());
}
TEST_F(DeviceStatusCollectorTest, DevSwitchBootMode) {
// Test that boot mode data is reported by default.
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey,
chromeos::system::kDevSwitchBootValueVerified);
GetStatus();
EXPECT_EQ("Verified", device_status_.boot_mode());
// Test that boot mode data is not reported if the pref turned off.
settings_helper_.SetBoolean(chromeos::kReportDeviceBootMode, false);
GetStatus();
EXPECT_FALSE(device_status_.has_boot_mode());
// Turn the pref on, and check that the status is reported iff the
// statistics provider returns valid data.
settings_helper_.SetBoolean(chromeos::kReportDeviceBootMode, true);
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey, "(error)");
GetStatus();
EXPECT_FALSE(device_status_.has_boot_mode());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey, " ");
GetStatus();
EXPECT_FALSE(device_status_.has_boot_mode());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey,
chromeos::system::kDevSwitchBootValueVerified);
GetStatus();
EXPECT_EQ("Verified", device_status_.boot_mode());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey,
chromeos::system::kDevSwitchBootValueDev);
GetStatus();
EXPECT_EQ("Dev", device_status_.boot_mode());
}
TEST_F(DeviceStatusCollectorTest, WriteProtectSwitch) {
// Test that write protect switch is reported by default.
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey,
chromeos::system::kFirmwareWriteProtectBootValueOn);
GetStatus();
EXPECT_TRUE(device_status_.write_protect_switch());
// Test that write protect switch is not reported if the hardware report pref
// is off.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_FALSE(device_status_.has_write_protect_switch());
// Turn the pref on, and check that the status is reported iff the
// statistics provider returns valid data.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, true);
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey, "(error)");
GetStatus();
EXPECT_FALSE(device_status_.has_write_protect_switch());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey, " ");
GetStatus();
EXPECT_FALSE(device_status_.has_write_protect_switch());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey,
chromeos::system::kFirmwareWriteProtectBootValueOn);
GetStatus();
EXPECT_TRUE(device_status_.write_protect_switch());
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey,
chromeos::system::kFirmwareWriteProtectBootValueOff);
GetStatus();
EXPECT_FALSE(device_status_.write_protect_switch());
}
TEST_F(DeviceStatusCollectorTest, VersionInfo) {
// Expect the version info to be reported by default.
GetStatus();
EXPECT_TRUE(device_status_.has_browser_version());
EXPECT_TRUE(device_status_.has_channel());
EXPECT_TRUE(device_status_.has_os_version());
EXPECT_TRUE(device_status_.has_firmware_version());
EXPECT_TRUE(device_status_.has_tpm_version_info());
// When the pref to collect this data is not enabled, expect that none of
// the fields are present in the protobuf.
settings_helper_.SetBoolean(chromeos::kReportDeviceVersionInfo, false);
GetStatus();
EXPECT_FALSE(device_status_.has_browser_version());
EXPECT_FALSE(device_status_.has_channel());
EXPECT_FALSE(device_status_.has_os_version());
EXPECT_FALSE(device_status_.has_firmware_version());
EXPECT_FALSE(device_status_.has_tpm_version_info());
settings_helper_.SetBoolean(chromeos::kReportDeviceVersionInfo, true);
GetStatus();
EXPECT_TRUE(device_status_.has_browser_version());
EXPECT_TRUE(device_status_.has_channel());
EXPECT_TRUE(device_status_.has_os_version());
EXPECT_TRUE(device_status_.has_firmware_version());
EXPECT_TRUE(device_status_.has_tpm_version_info());
// Check that the browser version is not empty. OS version & firmware don't
// have any reasonable values inside the unit test, so those aren't checked.
EXPECT_NE("", device_status_.browser_version());
}
TEST_F(DeviceStatusCollectorTest, ReportUsers) {
const AccountId public_account_id(
AccountId::FromUserEmail("public@localhost"));
const AccountId account_id0(AccountId::FromUserEmail("user0@managed.com"));
const AccountId account_id1(AccountId::FromUserEmail("user1@managed.com"));
const AccountId account_id2(AccountId::FromUserEmail("user2@managed.com"));
const AccountId account_id3(AccountId::FromUserEmail("user3@unmanaged.com"));
const AccountId account_id4(AccountId::FromUserEmail("user4@managed.com"));
const AccountId account_id5(AccountId::FromUserEmail("user5@managed.com"));
user_manager_->CreatePublicAccountUser(public_account_id);
user_manager_->AddUserWithAffiliationAndType(account_id0, true,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id1, true,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id2, true,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id3, false,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id4, true,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id5, true,
user_manager::USER_TYPE_REGULAR);
// Verify that users are reported by default.
GetStatus();
EXPECT_EQ(6, device_status_.user_size());
// Verify that users are reported after enabling the setting.
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
GetStatus();
EXPECT_EQ(6, device_status_.user_size());
EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, device_status_.user(0).type());
EXPECT_EQ(account_id0.GetUserEmail(), device_status_.user(0).email());
EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, device_status_.user(1).type());
EXPECT_EQ(account_id1.GetUserEmail(), device_status_.user(1).email());
EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, device_status_.user(2).type());
EXPECT_EQ(account_id2.GetUserEmail(), device_status_.user(2).email());
EXPECT_EQ(em::DeviceUser::USER_TYPE_UNMANAGED, device_status_.user(3).type());
EXPECT_FALSE(device_status_.user(3).has_email());
EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, device_status_.user(4).type());
EXPECT_EQ(account_id4.GetUserEmail(), device_status_.user(4).email());
EXPECT_EQ(em::DeviceUser::USER_TYPE_MANAGED, device_status_.user(5).type());
EXPECT_EQ(account_id5.GetUserEmail(), device_status_.user(5).email());
// Verify that users are no longer reported if setting is disabled.
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, false);
GetStatus();
EXPECT_EQ(0, device_status_.user_size());
}
TEST_F(DeviceStatusCollectorTest, TestVolumeInfo) {
std::vector<std::string> expected_mount_points;
std::vector<em::VolumeInfo> expected_volume_info;
int size = 12345678;
for (const auto& mount_info :
DiskMountManager::GetInstance()->mount_points()) {
expected_mount_points.push_back(mount_info.first);
}
expected_mount_points.push_back(kExternalMountPoint);
for (const std::string& mount_point : expected_mount_points) {
em::VolumeInfo info;
info.set_volume_id(mount_point);
// Just put unique numbers in for storage_total/free.
info.set_storage_total(size++);
info.set_storage_free(size++);
expected_volume_info.push_back(info);
}
EXPECT_FALSE(expected_volume_info.empty());
RestartStatusCollector(
base::BindRepeating(&GetFakeVolumeInfo, expected_volume_info),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::RunAllTasksUntilIdle();
GetStatus();
EXPECT_EQ(expected_mount_points.size(),
static_cast<size_t>(device_status_.volume_info_size()));
// Walk the returned VolumeInfo to make sure it matches.
for (const em::VolumeInfo& expected_info : expected_volume_info) {
bool found = false;
for (const em::VolumeInfo& info : device_status_.volume_info()) {
if (info.volume_id() == expected_info.volume_id()) {
EXPECT_EQ(expected_info.storage_total(), info.storage_total());
EXPECT_EQ(expected_info.storage_free(), info.storage_free());
found = true;
break;
}
}
EXPECT_TRUE(found) << "No matching VolumeInfo for "
<< expected_info.volume_id();
}
// Now turn off hardware status reporting - should have no data.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_EQ(0, device_status_.volume_info_size());
}
TEST_F(DeviceStatusCollectorTest, TestAvailableMemory) {
// Refresh our samples. Sample more than kMaxHardwareSamples times to
// make sure that the code correctly caps the number of cached samples.
for (int i = 0; i < static_cast<int>(
DeviceStatusCollector::kMaxResourceUsageSamples + 1);
++i) {
status_collector_->RefreshSampleResourceUsage();
base::RunLoop().RunUntilIdle();
}
GetStatus();
EXPECT_EQ(static_cast<int>(DeviceStatusCollector::kMaxResourceUsageSamples),
device_status_.system_ram_free().size());
EXPECT_TRUE(device_status_.has_system_ram_total());
// No good way to inject specific test values for available system RAM, so
// just make sure it's > 0.
EXPECT_GT(device_status_.system_ram_total(), 0);
}
TEST_F(DeviceStatusCollectorTest, TestCPUSamples) {
// Mock 100% CPU usage.
std::string full_cpu_usage("cpu 500 0 500 0 0 0 0");
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetFakeCPUStatistics, full_cpu_usage),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::RunAllTasksUntilIdle();
GetStatus();
ASSERT_EQ(1, device_status_.cpu_utilization_pct().size());
EXPECT_EQ(100, device_status_.cpu_utilization_pct(0));
// Now sample CPU usage again (active usage counters will not increase
// so should show 0% cpu usage).
status_collector_->RefreshSampleResourceUsage();
base::RunLoop().RunUntilIdle();
GetStatus();
ASSERT_EQ(2, device_status_.cpu_utilization_pct().size());
EXPECT_EQ(0, device_status_.cpu_utilization_pct(1));
// Now store a bunch of 0% cpu usage and make sure we cap the max number of
// samples.
for (int i = 0;
i < static_cast<int>(DeviceStatusCollector::kMaxResourceUsageSamples);
++i) {
status_collector_->RefreshSampleResourceUsage();
base::RunLoop().RunUntilIdle();
}
GetStatus();
// Should not be more than kMaxResourceUsageSamples, and they should all show
// the CPU is idle.
EXPECT_EQ(static_cast<int>(DeviceStatusCollector::kMaxResourceUsageSamples),
device_status_.cpu_utilization_pct().size());
for (const auto utilization : device_status_.cpu_utilization_pct())
EXPECT_EQ(0, utilization);
// Turning off hardware reporting should not report CPU utilization.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_EQ(0, device_status_.cpu_utilization_pct().size());
}
TEST_F(DeviceStatusCollectorTest, TestCPUTemp) {
std::vector<em::CPUTempInfo> expected_temp_info;
int cpu_cnt = 12;
for (int i = 0; i < cpu_cnt; ++i) {
em::CPUTempInfo info;
info.set_cpu_temp(i * 10 + 100);
info.set_cpu_label(base::StringPrintf("Core %d", i));
expected_temp_info.push_back(info);
}
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetFakeCPUTempInfo, expected_temp_info),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::RunAllTasksUntilIdle();
GetStatus();
EXPECT_EQ(expected_temp_info.size(),
static_cast<size_t>(device_status_.cpu_temp_info_size()));
// Walk the returned CPUTempInfo to make sure it matches.
for (const em::CPUTempInfo& expected_info : expected_temp_info) {
bool found = false;
for (const em::CPUTempInfo& info : device_status_.cpu_temp_info()) {
if (info.cpu_label() == expected_info.cpu_label()) {
EXPECT_EQ(expected_info.cpu_temp(), info.cpu_temp());
found = true;
break;
}
}
EXPECT_TRUE(found) << "No matching CPUTempInfo for "
<< expected_info.cpu_label();
}
// Now turn off hardware status reporting - should have no data.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_EQ(0, device_status_.cpu_temp_info_size());
}
TEST_F(DeviceStatusCollectorTest, KioskAndroidReporting) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
status_collector_->set_kiosk_account(
std::make_unique<DeviceLocalAccount>(fake_kiosk_device_local_account_));
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
testing_profile_->GetPrefs()->SetBoolean(prefs::kReportArcStatusEnabled,
true);
GetStatus();
EXPECT_EQ(kArcStatus, session_status_.android_status().status_payload());
EXPECT_EQ(kDroidGuardInfo,
session_status_.android_status().droid_guard_info());
// Expect no User DM Token for kiosk sessions.
EXPECT_FALSE(session_status_.has_user_dm_token());
}
TEST_F(DeviceStatusCollectorTest, NoKioskAndroidReportingWhenDisabled) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
// Mock Kiosk app, so some session status is reported
status_collector_->set_kiosk_account(
std::make_unique<DeviceLocalAccount>(fake_kiosk_device_local_account_));
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
GetStatus();
EXPECT_TRUE(got_session_status_);
// Note that this relies on the fact that kReportArcStatusEnabled is false by
// default.
EXPECT_FALSE(session_status_.has_android_status());
}
TEST_F(DeviceStatusCollectorTest, RegularUserAndroidReporting) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, true);
testing_profile_->GetPrefs()->SetBoolean(prefs::kReportArcStatusEnabled,
true);
GetStatus();
EXPECT_TRUE(got_session_status_);
EXPECT_EQ(kArcStatus, session_status_.android_status().status_payload());
EXPECT_EQ(kDroidGuardInfo,
session_status_.android_status().droid_guard_info());
// In tests, GetUserDMToken returns the e-mail for easy verification.
EXPECT_EQ(account_id.GetUserEmail(), session_status_.user_dm_token());
}
TEST_F(DeviceStatusCollectorTest, RegularUserCrostiniReporting) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, true);
testing_profile_->GetPrefs()->SetBoolean(
crostini::prefs::kReportCrostiniUsageEnabled, true);
testing_profile_->GetPrefs()->SetString(
crostini::prefs::kCrostiniLastLaunchVersion, "1.33.7");
testing_profile_->GetPrefs()->SetInt64(
crostini::prefs::kCrostiniLastLaunchTimeWindowStart, 1535760000000);
GetStatus();
EXPECT_TRUE(got_session_status_);
EXPECT_EQ(1535760000000, session_status_.crostini_status()
.last_launch_time_window_start_timestamp());
EXPECT_EQ("1.33.7",
session_status_.crostini_status().last_launch_vm_image_version());
// In tests, GetUserDMToken returns the e-mail for easy verification.
EXPECT_EQ(account_id.GetUserEmail(), session_status_.user_dm_token());
}
TEST_F(DeviceStatusCollectorTest, RegularUserCrostiniReportingNoData) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, true);
testing_profile_->GetPrefs()->SetBoolean(
crostini::prefs::kReportCrostiniUsageEnabled, true);
GetStatus();
// Currently, only AndroidStatus and Crostini usage reporting is done for
// regular users. If there is no reporting in both cases, no
// UserSessionStatusRequest is filled at all. Note that this test case relies
// on the fact that kReportArcStatusEnabled is false by default.
EXPECT_FALSE(got_session_status_);
}
TEST_F(DeviceStatusCollectorTest, NoRegularUserReportingByDefault) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, true);
GetStatus();
// Currently, only AndroidStatus and Crostini usage reporting is done for
// regular users. If both are disabled, no UserSessionStatusRequest is filled
// at all. Note that this test case relies on the fact that
// kReportArcStatusEnabled and kReportCrostiniUsageEnabled are false by
// default.
EXPECT_FALSE(got_session_status_);
}
TEST_F(DeviceStatusCollectorTest,
NoRegularUserAndroidReportingWhenNotAffiliated) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, false);
testing_profile_->GetPrefs()->SetBoolean(prefs::kReportArcStatusEnabled,
true);
GetStatus();
// Currently, only AndroidStatus reporting is done for regular users. If that
// is disabled, no UserSessionStatusRequest is filled at all.
EXPECT_FALSE(got_session_status_);
}
TEST_F(DeviceStatusCollectorTest, TpmStatusReporting) {
// Create a fake TPM status info and populate it with some random values.
const policy::TpmStatusInfo kFakeTpmStatus{
true, /* enabled */
false, /* owned */
true, /* initialized */
false, /* attestation_prepared */
true, /* attestation_enrolled */
5, /* dictionary_attack_counter */
10, /* dictionary_attack_threshold */
false, /* dictionary_attack_lockout_in_effect */
0, /* dictionary_attack_lockout_seconds_remaining */
true /* boot_lockbox_finalized */
};
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetFakeTpmStatus, kFakeTpmStatus));
GetStatus();
EXPECT_TRUE(device_status_.has_tpm_status_info());
EXPECT_EQ(kFakeTpmStatus.enabled, device_status_.tpm_status_info().enabled());
EXPECT_EQ(kFakeTpmStatus.owned, device_status_.tpm_status_info().owned());
EXPECT_EQ(kFakeTpmStatus.initialized,
device_status_.tpm_status_info().tpm_initialized());
EXPECT_EQ(kFakeTpmStatus.attestation_prepared,
device_status_.tpm_status_info().attestation_prepared());
EXPECT_EQ(kFakeTpmStatus.attestation_enrolled,
device_status_.tpm_status_info().attestation_enrolled());
EXPECT_EQ(kFakeTpmStatus.dictionary_attack_counter,
device_status_.tpm_status_info().dictionary_attack_counter());
EXPECT_EQ(kFakeTpmStatus.dictionary_attack_threshold,
device_status_.tpm_status_info().dictionary_attack_threshold());
EXPECT_EQ(
kFakeTpmStatus.dictionary_attack_lockout_in_effect,
device_status_.tpm_status_info().dictionary_attack_lockout_in_effect());
EXPECT_EQ(kFakeTpmStatus.dictionary_attack_lockout_seconds_remaining,
device_status_.tpm_status_info()
.dictionary_attack_lockout_seconds_remaining());
EXPECT_EQ(kFakeTpmStatus.boot_lockbox_finalized,
device_status_.tpm_status_info().boot_lockbox_finalized());
}
TEST_F(DeviceStatusCollectorTest, NoTimeZoneReporting) {
// Time zone is not reported in enterprise reports.
const AccountId account_id(AccountId::FromUserEmail("user0@managed.com"));
MockRegularUserWithAffiliation(account_id, true);
GetStatus();
EXPECT_FALSE(session_status_.has_time_zone());
}
TEST_F(DeviceStatusCollectorTest, NoSessionStatusIfNoSession) {
// Should not report session status if we don't have an active kiosk app or an
// active user session.
settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true);
GetStatus();
EXPECT_FALSE(got_session_status_);
}
TEST_F(DeviceStatusCollectorTest, NoSessionStatusIfSessionReportingDisabled) {
// Should not report session status if session status reporting is disabled.
settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, false);
// ReportDeviceSessionStatus only controls Kiosk reporting, ARC reporting
// has to be disabled serarately.
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_kiosk_device_local_account_));
// Set up a device-local account for single-app kiosk mode.
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
testing_profile_->GetPrefs()->SetBoolean(prefs::kReportArcStatusEnabled,
false);
GetStatus();
EXPECT_FALSE(got_session_status_);
}
TEST_F(DeviceStatusCollectorTest, ReportKioskSessionStatus) {
settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true);
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_kiosk_device_local_account_));
// Set up a device-local account for single-app kiosk mode.
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
GetStatus();
EXPECT_TRUE(got_session_status_);
ASSERT_EQ(1, session_status_.installed_apps_size());
EXPECT_EQ(kKioskAccountId, session_status_.device_local_account_id());
const em::AppStatus app = session_status_.installed_apps(0);
EXPECT_EQ(kKioskAppId, app.app_id());
// Test code just sets the version to the app ID.
EXPECT_EQ(kKioskAppId, app.extension_version());
EXPECT_FALSE(app.has_status());
EXPECT_FALSE(app.has_error());
// Expect no User DM Token for kiosk sessions.
EXPECT_FALSE(session_status_.has_user_dm_token());
}
TEST_F(DeviceStatusCollectorTest, ReportArcKioskSessionStatus) {
settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true);
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_arc_kiosk_device_local_account_));
// Set up a device-local account for single-app ARC kiosk mode.
MockRunningKioskApp(fake_arc_kiosk_device_local_account_,
true /* arc_kiosk */);
GetStatus();
EXPECT_TRUE(got_session_status_);
ASSERT_EQ(1, session_status_.installed_apps_size());
EXPECT_EQ(kArcKioskAccountId, session_status_.device_local_account_id());
const em::AppStatus app = session_status_.installed_apps(0);
EXPECT_EQ(kArcKioskPackageName, app.app_id());
EXPECT_TRUE(app.extension_version().empty());
EXPECT_FALSE(app.has_status());
EXPECT_FALSE(app.has_error());
// Expect no User DM Token for kiosk sessions.
EXPECT_FALSE(session_status_.has_user_dm_token());
}
TEST_F(DeviceStatusCollectorTest, NoOsUpdateStatusByDefault) {
MockPlatformVersion("1234.0.0");
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1234.0.0");
GetStatus();
EXPECT_FALSE(device_status_.has_os_update_status());
}
TEST_F(DeviceStatusCollectorTest, ReportOsUpdateStatusUpToDate) {
MockPlatformVersion("1234.0.0");
settings_helper_.SetBoolean(chromeos::kReportOsUpdateStatus, true);
const char* kRequiredPlatformVersions[] = {"1234", "1234.0", "1234.0.0"};
for (size_t i = 0; i < arraysize(kRequiredPlatformVersions); ++i) {
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, kRequiredPlatformVersions[i]);
GetStatus();
ASSERT_TRUE(device_status_.has_os_update_status())
<< "Required platform version=" << kRequiredPlatformVersions[i];
EXPECT_EQ(em::OsUpdateStatus::OS_UP_TO_DATE,
device_status_.os_update_status().update_status())
<< "Required platform version=" << kRequiredPlatformVersions[i];
EXPECT_EQ(kRequiredPlatformVersions[i],
device_status_.os_update_status().new_required_platform_version())
<< "Required platform version=" << kRequiredPlatformVersions[i];
}
}
TEST_F(DeviceStatusCollectorTest, ReportOsUpdateStatus) {
MockPlatformVersion("1234.0.0");
settings_helper_.SetBoolean(chromeos::kReportOsUpdateStatus, true);
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1235");
chromeos::UpdateEngineClient::Status update_status;
update_status.status = chromeos::UpdateEngineClient::UPDATE_STATUS_IDLE;
GetStatus();
ASSERT_TRUE(device_status_.has_os_update_status());
EXPECT_EQ(em::OsUpdateStatus::OS_IMAGE_DOWNLOAD_NOT_STARTED,
device_status_.os_update_status().update_status());
const chromeos::UpdateEngineClient::UpdateStatusOperation kUpdateEngineOps[] =
{
chromeos::UpdateEngineClient::UPDATE_STATUS_DOWNLOADING,
chromeos::UpdateEngineClient::UPDATE_STATUS_VERIFYING,
chromeos::UpdateEngineClient::UPDATE_STATUS_FINALIZING,
};
for (size_t i = 0; i < arraysize(kUpdateEngineOps); ++i) {
update_status.status = kUpdateEngineOps[i];
update_status.new_version = "1235.1.2";
update_engine_client_->PushLastStatus(update_status);
GetStatus();
ASSERT_TRUE(device_status_.has_os_update_status());
EXPECT_EQ(em::OsUpdateStatus::OS_IMAGE_DOWNLOAD_IN_PROGRESS,
device_status_.os_update_status().update_status());
EXPECT_EQ("1235.1.2",
device_status_.os_update_status().new_platform_version());
EXPECT_EQ(
"1235",
device_status_.os_update_status().new_required_platform_version());
}
update_status.status =
chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT;
update_engine_client_->PushLastStatus(update_status);
GetStatus();
ASSERT_TRUE(device_status_.has_os_update_status());
EXPECT_EQ(em::OsUpdateStatus::OS_UPDATE_NEED_REBOOT,
device_status_.os_update_status().update_status());
}
TEST_F(DeviceStatusCollectorTest, NoRunningKioskAppByDefault) {
MockPlatformVersion("1234.0.0");
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1234.0.0");
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_kiosk_device_local_account_));
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
GetStatus();
EXPECT_FALSE(device_status_.has_running_kiosk_app());
}
TEST_F(DeviceStatusCollectorTest, NoRunningKioskAppWhenNotInKioskSession) {
settings_helper_.SetBoolean(chromeos::kReportRunningKioskApp, true);
MockPlatformVersion("1234.0.0");
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1234.0.0");
GetStatus();
EXPECT_FALSE(device_status_.has_running_kiosk_app());
}
TEST_F(DeviceStatusCollectorTest, ReportRunningKioskApp) {
settings_helper_.SetBoolean(chromeos::kReportRunningKioskApp, true);
MockPlatformVersion("1234.0.0");
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1235");
MockRunningKioskApp(fake_kiosk_device_local_account_, false /* arc_kiosk */);
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_kiosk_device_local_account_));
GetStatus();
ASSERT_TRUE(device_status_.has_running_kiosk_app());
const em::AppStatus app = device_status_.running_kiosk_app();
EXPECT_EQ(kKioskAppId, app.app_id());
EXPECT_EQ("1235", app.required_platform_version());
EXPECT_FALSE(app.has_status());
EXPECT_FALSE(app.has_error());
}
TEST_F(DeviceStatusCollectorTest, ReportRunningArcKioskApp) {
settings_helper_.SetBoolean(chromeos::kReportRunningKioskApp, true);
MockAutoLaunchArcKioskApp(fake_arc_kiosk_device_local_account_);
MockRunningKioskApp(fake_arc_kiosk_device_local_account_,
true /* arc_kiosk */);
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_arc_kiosk_device_local_account_));
GetStatus();
ASSERT_TRUE(device_status_.has_running_kiosk_app());
const em::AppStatus app = device_status_.running_kiosk_app();
EXPECT_EQ(kArcKioskPackageName, app.app_id());
EXPECT_TRUE(app.extension_version().empty());
EXPECT_TRUE(app.required_platform_version().empty());
EXPECT_FALSE(app.has_status());
EXPECT_FALSE(app.has_error());
}
TEST_F(DeviceStatusCollectorTest, TestSoundVolume) {
// Expect the sound volume to be reported by default (default sound volume
// used in testing is 75).
GetStatus();
EXPECT_EQ(75, device_status_.sound_volume());
// When the pref to collect this data is not enabled, expect that the field
// isn't present in the protobuf.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_FALSE(device_status_.has_sound_volume());
// Try setting a custom volume value and check that it matches.
const int kCustomVolume = 42;
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, true);
chromeos::CrasAudioHandler::Get()->SetOutputVolumePercent(kCustomVolume);
GetStatus();
EXPECT_EQ(kCustomVolume, device_status_.sound_volume());
}
// Fake device state.
struct FakeDeviceData {
const char* device_path;
const char* type;
const char* object_path;
const char* mac_address;
const char* meid;
const char* imei;
int expected_type; // proto enum type value, -1 for not present.
};
static const FakeDeviceData kFakeDevices[] = {
{ "/device/ethernet", shill::kTypeEthernet, "ethernet",
"112233445566", "", "",
em::NetworkInterface::TYPE_ETHERNET },
{ "/device/cellular1", shill::kTypeCellular, "cellular1",
"abcdefabcdef", "A10000009296F2", "",
em::NetworkInterface::TYPE_CELLULAR },
{ "/device/cellular2", shill::kTypeCellular, "cellular2",
"abcdefabcdef", "", "352099001761481",
em::NetworkInterface::TYPE_CELLULAR },
{ "/device/wifi", shill::kTypeWifi, "wifi",
"aabbccddeeff", "", "",
em::NetworkInterface::TYPE_WIFI },
{ "/device/bluetooth", shill::kTypeBluetooth, "bluetooth",
"", "", "",
em::NetworkInterface::TYPE_BLUETOOTH },
{ "/device/vpn", shill::kTypeVPN, "vpn",
"", "", "",
-1 },
};
// Fake network state.
struct FakeNetworkState {
const char* name;
const char* device_path;
const char* type;
int signal_strength;
int expected_signal_strength;
const char* connection_status;
int expected_state;
const char* address;
const char* gateway;
};
// List of fake networks - primarily used to make sure that signal strength
// and connection state are properly populated in status reports. Note that
// by convention shill will not report a signal strength of 0 for a visible
// network, so we use 1 below.
static const FakeNetworkState kFakeNetworks[] = {
{ "offline", "/device/wifi", shill::kTypeWifi, 35, -85,
shill::kStateOffline, em::NetworkState::OFFLINE, "", "" },
{ "ethernet", "/device/ethernet", shill::kTypeEthernet, 0, 0,
shill::kStateOnline, em::NetworkState::ONLINE,
"192.168.0.1", "8.8.8.8" },
{ "wifi", "/device/wifi", shill::kTypeWifi, 23, -97, shill::kStatePortal,
em::NetworkState::PORTAL, "", "" },
{ "idle", "/device/cellular1", shill::kTypeCellular, 0, 0, shill::kStateIdle,
em::NetworkState::IDLE, "", "" },
{ "carrier", "/device/cellular1", shill::kTypeCellular, 0, 0,
shill::kStateCarrier, em::NetworkState::CARRIER, "", "" },
{ "association", "/device/cellular1", shill::kTypeCellular, 0, 0,
shill::kStateAssociation, em::NetworkState::ASSOCIATION, "", "" },
{ "config", "/device/cellular1", shill::kTypeCellular, 0, 0,
shill::kStateConfiguration, em::NetworkState::CONFIGURATION, "", "" },
// Set signal strength for this network to -20, but expected strength to 0
// to test that we only report signal_strength for wifi connections.
{ "ready", "/device/cellular1", shill::kTypeCellular, -20, 0,
shill::kStateReady, em::NetworkState::READY, "", "" },
{ "disconnect", "/device/wifi", shill::kTypeWifi, 1, -119,
shill::kStateDisconnect, em::NetworkState::DISCONNECT, "", "" },
{ "failure", "/device/wifi", shill::kTypeWifi, 1, -119, shill::kStateFailure,
em::NetworkState::FAILURE, "", "" },
{ "activation-failure", "/device/cellular1", shill::kTypeCellular, 0, 0,
shill::kStateActivationFailure, em::NetworkState::ACTIVATION_FAILURE,
"", "" },
{ "unknown", "", shill::kTypeWifi, 1, -119, "unknown",
em::NetworkState::UNKNOWN, "", "" },
};
static const FakeNetworkState kUnconfiguredNetwork = {
"unconfigured", "/device/unconfigured", shill::kTypeWifi, 35, -85,
shill::kStateOffline, em::NetworkState::OFFLINE, "", ""
};
// Tests activity reporting day start correctness.
class DeviceStatusCollectorDayStartTest : public DeviceStatusCollectorTest {
protected:
DeviceStatusCollectorDayStartTest() = default;
~DeviceStatusCollectorDayStartTest() override = default;
void SetUp() override {
DeviceStatusCollectorTest::SetUp();
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
}
// Restarts device status collector for activity reporting tests with given
// |activity_day_start|.
void RestartStatusCollectorWithDayStart(TimeDelta activity_day_start) {
status_collector_ = std::make_unique<TestingDeviceStatusCollector>(
&local_state_, &fake_statistics_provider_,
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus), activity_day_start,
true /* is_enterprise_reporting */);
}
// Sets current test time to |time_since_midnight|.
void SetCurrentTime(TimeDelta time_since_midnight) {
status_collector_->SetBaselineTime(Time::Now().LocalMidnight() +
time_since_midnight);
}
private:
DISALLOW_COPY_AND_ASSIGN(DeviceStatusCollectorDayStartTest);
};
TEST_F(DeviceStatusCollectorDayStartTest, ArbitraryActivityDayStart) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_IDLE,
ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE};
const TimeDelta kNoon = TimeDelta::FromHours(12);
RestartStatusCollectorWithDayStart(kNoon);
// Test a single active sample.
status_collector_->Simulate(test_states, 1);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
device_status_.clear_active_period(); // Clear the result protobuf.
// Test multiple consecutive active samples.
status_collector_->Simulate(test_states, 4);
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
}
TEST_F(DeviceStatusCollectorDayStartTest, ActivityCrossingDayStart) {
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE};
const TimeDelta kDayStart = TimeDelta::FromHours(6);
RestartStatusCollectorWithDayStart(kDayStart);
// Set time to 10 seconds after day start.
SetCurrentTime(kDayStart + TimeDelta::FromSeconds(10));
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
em::ActiveTimePeriod period0 = device_status_.active_period(0);
em::ActiveTimePeriod period1 = device_status_.active_period(1);
EXPECT_EQ(ActivePeriodMilliseconds() - 10000, period0.active_duration());
EXPECT_EQ(10000, period1.active_duration());
em::TimePeriod time_period0 = period0.time_period();
em::TimePeriod time_period1 = period1.time_period();
EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp());
// Ensure that the start and end times for the period are a day apart.
EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(),
kMillisecondsPerDay);
EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(),
kMillisecondsPerDay);
}
TEST_F(DeviceStatusCollectorDayStartTest, ActivityDayStartChangesToLater) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
};
const TimeDelta kDayStart; /* Midnight */
RestartStatusCollectorWithDayStart(kDayStart);
// Set clock to 1h after day start and report 2 activities.
SetCurrentTime(kDayStart + kHour);
status_collector_->Simulate(test_states, 2);
// Move day starts to later hour.
const TimeDelta kLaterDayStart = kDayStart + TimeDelta::FromHours(6);
RestartStatusCollectorWithDayStart(kLaterDayStart);
// Set clock before day start and report 1 activity.
SetCurrentTime(kLaterDayStart - kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
// Set clock after day start and report 1 activity.
SetCurrentTime(kLaterDayStart + kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(3, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(2).active_duration());
}
TEST_F(DeviceStatusCollectorDayStartTest, ActivityDayStartChangesToEarlier) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
};
const TimeDelta kDayStart = TimeDelta::FromHours(6);
RestartStatusCollectorWithDayStart(kDayStart);
// Set clock after day start and report 2 activities.
SetCurrentTime(kDayStart + kHour);
status_collector_->Simulate(test_states, 2);
// Move day starts to earlier hour.
const TimeDelta kEarlierDayStart = kDayStart - TimeDelta::FromHours(3);
RestartStatusCollectorWithDayStart(kEarlierDayStart);
// Set clock before day start and report 1 activity.
SetCurrentTime(kEarlierDayStart - kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
// Set clock after day start and report 1 activity.
SetCurrentTime(kEarlierDayStart + kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(3, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
device_status_.active_period(2).active_duration());
}
TEST_F(DeviceStatusCollectorDayStartTest,
ActivityDayStartGetsBackToTheSameValue) {
ui::IdleState test_states[] = {
ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
};
const TimeDelta kDayStart = TimeDelta::FromHours(0);
RestartStatusCollectorWithDayStart(kDayStart);
// Set clock after day start report 2 activities.
SetCurrentTime(kDayStart + kHour);
status_collector_->Simulate(test_states, 2);
// Move day starts to later hour.
const TimeDelta kLaterDayStart = kDayStart + TimeDelta::FromHours(6);
RestartStatusCollectorWithDayStart(kLaterDayStart);
// Set clock after day start and report 1 activity.
SetCurrentTime(kLaterDayStart + kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(2 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
// Move day start back.
RestartStatusCollectorWithDayStart(kDayStart);
// Progress clock from the previous report.
SetCurrentTime(kLaterDayStart + 2 * kHour);
status_collector_->Simulate(test_states, 1);
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
EXPECT_EQ(3 * ActivePeriodMilliseconds(),
device_status_.active_period(0).active_duration());
EXPECT_EQ(1 * ActivePeriodMilliseconds(),
device_status_.active_period(1).active_duration());
}
class DeviceStatusCollectorNetworkInterfacesTest
: public DeviceStatusCollectorTest {
protected:
void SetUp() override {
RestartStatusCollector(base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
chromeos::DBusThreadManager::Initialize();
chromeos::NetworkHandler::Initialize();
base::RunLoop().RunUntilIdle();
chromeos::ShillDeviceClient::TestInterface* device_client =
chromeos::DBusThreadManager::Get()
->GetShillDeviceClient()
->GetTestInterface();
chromeos::ShillServiceClient::TestInterface* service_client =
chromeos::DBusThreadManager::Get()
->GetShillServiceClient()
->GetTestInterface();
chromeos::ShillIPConfigClient::TestInterface* ip_config_client =
chromeos::DBusThreadManager::Get()
->GetShillIPConfigClient()
->GetTestInterface();
device_client->ClearDevices();
service_client->ClearServices();
for (const FakeDeviceData& dev : kFakeDevices) {
device_client->AddDevice(dev.device_path, dev.type, dev.object_path);
if (*dev.mac_address) {
device_client->SetDeviceProperty(
dev.device_path, shill::kAddressProperty,
base::Value(dev.mac_address), /*notify_changed=*/true);
}
if (*dev.meid) {
device_client->SetDeviceProperty(dev.device_path, shill::kMeidProperty,
base::Value(dev.meid),
/*notify_changed=*/true);
}
if (*dev.imei) {
device_client->SetDeviceProperty(dev.device_path, shill::kImeiProperty,
base::Value(dev.imei),
/*notify_changed=*/true);
}
}
chromeos::DBusThreadManager::Get()
->GetShillProfileClient()
->GetTestInterface()
->AddProfile(kShillFakeProfilePath, kShillFakeUserhash);
// Now add services for every fake network.
for (const FakeNetworkState& fake_network : kFakeNetworks) {
// Shill forces non-visible networks to report a disconnected state.
bool is_visible =
fake_network.connection_status != shill::kStateDisconnect;
service_client->AddService(fake_network.name /* service_path */,
fake_network.name /* guid */,
fake_network.name, fake_network.type,
fake_network.connection_status, is_visible);
service_client->SetServiceProperty(
fake_network.name, shill::kSignalStrengthProperty,
base::Value(fake_network.signal_strength));
service_client->SetServiceProperty(fake_network.name,
shill::kDeviceProperty,
base::Value(fake_network.device_path));
// Set the profile so this shows up as a configured network.
service_client->SetServiceProperty(fake_network.name,
shill::kProfileProperty,
base::Value(kShillFakeProfilePath));
if (strlen(fake_network.address) > 0) {
// Set the IP config.
base::DictionaryValue ip_config_properties;
ip_config_properties.SetKey(shill::kAddressProperty,
base::Value(fake_network.address));
ip_config_properties.SetKey(shill::kGatewayProperty,
base::Value(fake_network.gateway));
const std::string kIPConfigPath = "test_ip_config";
ip_config_client->AddIPConfig(kIPConfigPath, ip_config_properties);
service_client->SetServiceProperty(fake_network.name,
shill::kIPConfigProperty,
base::Value(kIPConfigPath));
}
}
// Now add an unconfigured network - it should not show up in the
// reported list of networks because it doesn't have a profile specified.
service_client->AddService(kUnconfiguredNetwork.name, /* service_path */
kUnconfiguredNetwork.name /* guid */,
kUnconfiguredNetwork.name /* name */,
kUnconfiguredNetwork.type /* type */,
kUnconfiguredNetwork.connection_status,
true /* visible */);
service_client->SetServiceProperty(
kUnconfiguredNetwork.name, shill::kSignalStrengthProperty,
base::Value(kUnconfiguredNetwork.signal_strength));
service_client->SetServiceProperty(
kUnconfiguredNetwork.name, shill::kDeviceProperty,
base::Value(kUnconfiguredNetwork.device_path));
// Flush out pending state updates.
base::RunLoop().RunUntilIdle();
chromeos::NetworkStateHandler::NetworkStateList state_list;
chromeos::NetworkStateHandler* network_state_handler =
chromeos::NetworkHandler::Get()->network_state_handler();
network_state_handler->GetNetworkListByType(
chromeos::NetworkTypePattern::Default(),
true, // configured_only
false, // visible_only,
0, // no limit to number of results
&state_list);
ASSERT_EQ(arraysize(kFakeNetworks), state_list.size());
}
void TearDown() override {
chromeos::NetworkHandler::Shutdown();
}
void VerifyNetworkReporting() {
int count = 0;
for (const FakeDeviceData& dev : kFakeDevices) {
if (dev.expected_type == -1)
continue;
// Find the corresponding entry in reporting data.
bool found_match = false;
google::protobuf::RepeatedPtrField<em::NetworkInterface>::const_iterator
iface;
for (iface = device_status_.network_interface().begin();
iface != device_status_.network_interface().end(); ++iface) {
// Check whether type, field presence and field values match.
if (dev.expected_type == iface->type() &&
iface->has_mac_address() == !!*dev.mac_address &&
iface->has_meid() == !!*dev.meid &&
iface->has_imei() == !!*dev.imei &&
iface->mac_address() == dev.mac_address &&
iface->meid() == dev.meid && iface->imei() == dev.imei &&
iface->device_path() == dev.device_path) {
found_match = true;
break;
}
}
EXPECT_TRUE(found_match)
<< "No matching interface for fake device " << dev.device_path;
count++;
}
EXPECT_EQ(count, device_status_.network_interface_size());
// Now make sure network state list is correct.
EXPECT_EQ(arraysize(kFakeNetworks),
static_cast<size_t>(device_status_.network_state_size()));
for (const FakeNetworkState& state : kFakeNetworks) {
bool found_match = false;
for (const em::NetworkState& proto_state :
device_status_.network_state()) {
// Make sure every item has a matching entry in the proto.
bool should_have_signal_strength = state.expected_signal_strength != 0;
if (proto_state.has_device_path() == (strlen(state.device_path) > 0) &&
proto_state.has_signal_strength() == should_have_signal_strength &&
proto_state.signal_strength() == state.expected_signal_strength &&
proto_state.connection_state() == state.expected_state) {
if (proto_state.has_ip_address())
EXPECT_EQ(proto_state.ip_address(), state.address);
else
EXPECT_EQ(0U, strlen(state.address));
if (proto_state.has_gateway())
EXPECT_EQ(proto_state.gateway(), state.gateway);
else
EXPECT_EQ(0U, strlen(state.gateway));
found_match = true;
break;
}
}
EXPECT_TRUE(found_match) << "No matching state for fake network "
<< " (" << state.name << ")";
}
}
};
TEST_F(DeviceStatusCollectorNetworkInterfacesTest, NoNetworkStateIfNotKiosk) {
// If not in an active kiosk session, there should be network interfaces
// reported, but no network state.
GetStatus();
EXPECT_LT(0, device_status_.network_interface_size());
EXPECT_EQ(0, device_status_.network_state_size());
}
TEST_F(DeviceStatusCollectorNetworkInterfacesTest, NetworkInterfaces) {
// Mock that we are in kiosk mode so we report network state.
status_collector_->set_kiosk_account(
std::make_unique<policy::DeviceLocalAccount>(
fake_kiosk_device_local_account_));
// Interfaces should be reported by default.
GetStatus();
EXPECT_LT(0, device_status_.network_interface_size());
EXPECT_LT(0, device_status_.network_state_size());
// No interfaces should be reported if the policy is off.
settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, false);
GetStatus();
EXPECT_EQ(0, device_status_.network_interface_size());
EXPECT_EQ(0, device_status_.network_state_size());
// Switch the policy on and verify the interface list is present.
settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, true);
GetStatus();
VerifyNetworkReporting();
}
TEST_F(DeviceStatusCollectorNetworkInterfacesTest, ReportIfPublicSession) {
// Report netowork state for public accounts.
user_manager_->CreatePublicAccountUser(
AccountId::FromUserEmail(kPublicAccountId));
EXPECT_CALL(*user_manager_, IsLoggedInAsPublicAccount())
.WillRepeatedly(Return(true));
settings_helper_.SetBoolean(chromeos::kReportDeviceNetworkInterfaces, true);
GetStatus();
VerifyNetworkReporting();
}
// Tests collecting device status for registered consumer device.
class ConsumerDeviceStatusCollectorTimeLimitDisabledTest
: public DeviceStatusCollectorTest {
public:
ConsumerDeviceStatusCollectorTimeLimitDisabledTest() {
user_account_id_ = AccountId::FromUserEmail("user0@gmail.com");
MockChildUser(user_account_id_);
scoped_feature_list_.InitAndDisableFeature(features::kUsageTimeLimitPolicy);
}
~ConsumerDeviceStatusCollectorTimeLimitDisabledTest() override = default;
protected:
void RestartStatusCollector(
const policy::DeviceStatusCollector::VolumeInfoFetcher& volume_info,
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_stats,
const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher,
const policy::DeviceStatusCollector::AndroidStatusFetcher&
android_status_fetcher,
const policy::DeviceStatusCollector::TpmStatusFetcher& tpm_status_fetcher,
const TimeDelta activity_day_start = kMidnight) override {
status_collector_ = std::make_unique<TestingDeviceStatusCollector>(
&profile_pref_service_, &fake_statistics_provider_, volume_info,
cpu_stats, cpu_temp_fetcher, android_status_fetcher, tpm_status_fetcher,
activity_day_start, false /* is_enterprise_reporting */);
}
void ExpectChildScreenTimeMilliseconds(int64_t duration) {
profile_pref_service_.CommitPendingWrite(
base::OnceClosure(),
base::BindOnce(
[](int64_t duration,
TestingPrefServiceSimple* profile_pref_service_) {
EXPECT_EQ(duration, profile_pref_service_->GetInteger(
prefs::kChildScreenTimeMilliseconds));
},
duration, &profile_pref_service_));
}
void ExpectLastChildScreenTimeReset(Time time) {
profile_pref_service_.CommitPendingWrite(
base::OnceClosure(),
base::BindOnce(
[](Time time, TestingPrefServiceSimple* profile_pref_service_) {
EXPECT_EQ(time, profile_pref_service_->GetTime(
prefs::kLastChildScreenTimeReset));
},
time, &profile_pref_service_));
}
AccountId user_account_id_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest, ReportingBootMode) {
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kDevSwitchBootKey,
chromeos::system::kDevSwitchBootValueVerified);
GetStatus();
EXPECT_TRUE(device_status_.has_boot_mode());
EXPECT_EQ("Verified", device_status_.boot_mode());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
NotReportingWriteProtectSwitch) {
fake_statistics_provider_.SetMachineStatistic(
chromeos::system::kFirmwareWriteProtectBootKey,
chromeos::system::kFirmwareWriteProtectBootValueOn);
GetStatus();
EXPECT_FALSE(device_status_.has_write_protect_switch());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest, ReportingArcStatus) {
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetFakeAndroidStatus, kArcStatus, kDroidGuardInfo),
base::BindRepeating(&GetEmptyTpmStatus));
testing_profile_->GetPrefs()->SetBoolean(prefs::kReportArcStatusEnabled,
true);
GetStatus();
EXPECT_EQ(kArcStatus, session_status_.android_status().status_payload());
EXPECT_EQ(kDroidGuardInfo,
session_status_.android_status().droid_guard_info());
// In tests, GetUserDMToken returns the e-mail for easy verification.
EXPECT_EQ(user_account_id_.GetUserEmail(), session_status_.user_dm_token());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
ReportingPartialVersionInfo) {
GetStatus();
// Should only report OS version.
EXPECT_TRUE(device_status_.has_os_version());
EXPECT_FALSE(device_status_.has_browser_version());
EXPECT_FALSE(device_status_.has_channel());
EXPECT_FALSE(device_status_.has_firmware_version());
EXPECT_FALSE(device_status_.has_tpm_version_info());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
NotReportingVolumeInfo) {
std::vector<std::string> expected_mount_points;
std::vector<em::VolumeInfo> expected_volume_info;
for (const auto& mount_info :
DiskMountManager::GetInstance()->mount_points()) {
expected_mount_points.push_back(mount_info.first);
}
expected_mount_points.push_back(kExternalMountPoint);
for (const std::string& mount_point : expected_mount_points) {
em::VolumeInfo info;
info.set_volume_id(mount_point);
info.set_storage_total(12345678);
info.set_storage_free(1234567);
expected_volume_info.push_back(info);
}
EXPECT_FALSE(expected_volume_info.empty());
RestartStatusCollector(
base::BindRepeating(&GetFakeVolumeInfo, expected_volume_info),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
content::RunAllTasksUntilIdle();
GetStatus();
EXPECT_EQ(0, device_status_.volume_info_size());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest, NotReportingUsers) {
const AccountId account_id0(AccountId::FromUserEmail("user0@gmail.com"));
const AccountId account_id1(AccountId::FromUserEmail("user1@gmail.com"));
user_manager_->AddUserWithAffiliationAndType(account_id0, true,
user_manager::USER_TYPE_REGULAR);
user_manager_->AddUserWithAffiliationAndType(account_id1, true,
user_manager::USER_TYPE_CHILD);
GetStatus();
EXPECT_EQ(0, device_status_.user_size());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
NotReportingOSUpdateStatus) {
MockPlatformVersion("1234.0.0");
MockAutoLaunchKioskAppWithRequiredPlatformVersion(
fake_kiosk_device_local_account_, "1234.0.0");
GetStatus();
EXPECT_FALSE(device_status_.has_os_update_status());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
NotReportingDeviceHardwareStatus) {
const std::string full_cpu_usage("cpu 500 0 500 0");
std::vector<em::CPUTempInfo> expected_temp_info;
for (int i = 0; i < 5; ++i) {
em::CPUTempInfo info;
info.set_cpu_temp(100);
info.set_cpu_label("Core");
expected_temp_info.push_back(info);
}
status_collector_->RefreshSampleResourceUsage();
RestartStatusCollector(
base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetFakeCPUStatistics, full_cpu_usage),
base::BindRepeating(&GetFakeCPUTempInfo, expected_temp_info),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
content::RunAllTasksUntilIdle();
GetStatus();
EXPECT_FALSE(device_status_.has_sound_volume());
EXPECT_EQ(0, device_status_.cpu_utilization_pct().size());
EXPECT_EQ(0, device_status_.cpu_temp_info_size());
EXPECT_EQ(0, device_status_.system_ram_free().size());
EXPECT_FALSE(device_status_.has_system_ram_total());
EXPECT_FALSE(device_status_.has_tpm_status_info());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest, TimeZoneReporting) {
const std::string timezone =
base::UTF16ToUTF8(chromeos::system::TimezoneSettings::GetInstance()
->GetCurrentTimezoneID());
GetStatus();
EXPECT_TRUE(session_status_.has_time_zone());
EXPECT_EQ(timezone, session_status_.time_zone());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitDisabledTest,
ActivityTimesFeatureDisable) {
settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
settings_helper_.SetBoolean(chromeos::kReportDeviceUsers, true);
ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
ui::IDLE_STATE_ACTIVE};
status_collector_->Simulate(test_states, 3);
GetStatus();
EXPECT_EQ(0, device_status_.active_period_size());
}
// Tests collecting device status for registered consumer device when time
// limit feature is enabled.
class ConsumerDeviceStatusCollectorTimeLimitEnabledTest
: public ConsumerDeviceStatusCollectorTimeLimitDisabledTest {
public:
ConsumerDeviceStatusCollectorTimeLimitEnabledTest() {
scoped_feature_list_.InitAndEnableFeature(features::kUsageTimeLimitPolicy);
}
~ConsumerDeviceStatusCollectorTimeLimitEnabledTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Fails on all chromeos builders https://crbug.com/891573
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
DISABLED_ReportingActivityTimesSessionTransistions) {
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
ASSERT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(5 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(5 * ActivePeriodMilliseconds());
EXPECT_EQ(user_account_id_.GetUserEmail(),
device_status_.active_period(0).user_email());
}
// Fails on all chromeos builders https://crbug.com/891573
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
DISABLED_ReportingActivityTimesSleepTransistions) {
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kEnterSleep,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kLeaveSleep,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
ASSERT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(4 * ActivePeriodMilliseconds());
EXPECT_EQ(user_account_id_.GetUserEmail(),
device_status_.active_period(0).user_email());
}
// Fails on all chromeos builders https://crbug.com/891573
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
DISABLED_ReportingActivityTimesIdleTransitions) {
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kEnterIdleState,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kLeaveIdleState,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
ASSERT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(5 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(5 * ActivePeriodMilliseconds());
EXPECT_EQ(user_account_id_.GetUserEmail(),
device_status_.active_period(0).user_email());
}
// Fails on all chromeos builders https://crbug.com/891573
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
DISABLED_ActivityKeptInPref) {
EXPECT_TRUE(
profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
EXPECT_FALSE(
profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
// Process the list a second time after restarting the collector. It should be
// able to count the active periods found by the original collector, because
// the results are stored in a pref.
RestartStatusCollector(base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus));
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
EXPECT_EQ(12 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(12 * ActivePeriodMilliseconds());
}
// Fails on all chromeos builders https://crbug.com/891573
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
DISABLED_ActivityNotWrittenToLocalState) {
EXPECT_TRUE(local_state_.GetDictionary(prefs::kDeviceActivityTimes)->empty());
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered, // Check while inactive
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
EXPECT_EQ(1, device_status_.active_period_size());
EXPECT_EQ(5 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(5 * ActivePeriodMilliseconds());
// Nothing should be written to local state, because it is only used for
// enterprise reporting.
EXPECT_TRUE(local_state_.GetDictionary(prefs::kDeviceActivityTimes)->empty());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest, BeforeDayStart) {
RestartStatusCollector(base::BindRepeating(&GetEmptyVolumeInfo),
base::BindRepeating(&GetEmptyCPUStatistics),
base::BindRepeating(&GetEmptyCPUTempInfo),
base::BindRepeating(&GetEmptyAndroidStatus),
base::BindRepeating(&GetEmptyTpmStatus), kSixAm);
// 04:00 AM
Time initial_time = Time::Now().LocalMidnight() + TimeDelta::FromHours(4);
status_collector_->SetBaselineTime(initial_time);
EXPECT_TRUE(
profile_pref_service_.GetDictionary(prefs::kUserActivityTimes)->empty());
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kLeaveSessionActive,
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kPeriodicCheckTriggered,
DeviceStateTransitions::kLeaveSessionActive};
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
// 4 is the number of states yielding an active period with duration of
// ActivePeriodMilliseconds
EXPECT_EQ(4 * ActivePeriodMilliseconds(),
GetActiveMilliseconds(device_status_));
ExpectChildScreenTimeMilliseconds(4 * ActivePeriodMilliseconds());
ExpectLastChildScreenTimeReset(initial_time);
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest,
ActivityCrossingMidnight) {
DeviceStateTransitions test_states[] = {
DeviceStateTransitions::kEnterSessionActive,
DeviceStateTransitions::kLeaveSessionActive};
// Set the baseline time to 15 seconds before midnight, so the activity is
// split between two days.
status_collector_->SetBaselineTime(Time::Now().LocalMidnight() -
TimeDelta::FromSeconds(15));
SimulateStateChanges(test_states,
sizeof(test_states) / sizeof(DeviceStateTransitions));
GetStatus();
ASSERT_EQ(2, device_status_.active_period_size());
em::ActiveTimePeriod period0 = device_status_.active_period(0);
em::ActiveTimePeriod period1 = device_status_.active_period(1);
EXPECT_EQ(ActivePeriodMilliseconds() - 15000, period0.active_duration());
EXPECT_EQ(15000, period1.active_duration());
em::TimePeriod time_period0 = period0.time_period();
em::TimePeriod time_period1 = period1.time_period();
EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp());
// Ensure that the start and end times for the period are a day apart.
EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(),
kMillisecondsPerDay);
EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(),
kMillisecondsPerDay);
ExpectChildScreenTimeMilliseconds(0.5 * ActivePeriodMilliseconds());
}
TEST_F(ConsumerDeviceStatusCollectorTimeLimitEnabledTest, ClockChanged) {
DeviceStateTransitions test_states[1] = {
DeviceStateTransitions::kEnterSessionActive};
base::Time initial_time =
Time::Now().LocalMidnight() + base::TimeDelta::FromHours(1);
status_collector_->SetBaselineTime(initial_time);
SimulateStateChanges(test_states, 1);
// Simulate clock change.
status_collector_->SetBaselineTime(initial_time - TimeDelta::FromMinutes(30));
test_states[0] = DeviceStateTransitions::kLeaveSessionActive;
SimulateStateChanges(test_states, 1);
GetStatus();
ASSERT_EQ(1, device_status_.active_period_size());
ExpectChildScreenTimeMilliseconds(ActivePeriodMilliseconds());
}
} // namespace policy