|  | // Copyright 2015 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chrome/browser/ash/system/system_clock.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/logging.h" | 
|  | #include "chrome/browser/ash/ownership/owner_settings_service_ash.h" | 
|  | #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h" | 
|  | #include "chrome/browser/ash/profiles/profile_helper.h" | 
|  | #include "chrome/browser/ash/system/system_clock_observer.h" | 
|  | #include "chrome/common/pref_names.h" | 
|  | #include "chromeos/ash/components/browser_context_helper/annotated_account_id.h" | 
|  | #include "chromeos/ash/components/install_attributes/install_attributes.h" | 
|  | #include "chromeos/ash/components/settings/cros_settings.h" | 
|  | #include "chromeos/ash/components/settings/cros_settings_names.h" | 
|  | #include "components/account_id/account_id.h" | 
|  | #include "components/prefs/pref_change_registrar.h" | 
|  | #include "components/prefs/pref_service.h" | 
|  | #include "components/user_manager/user.h" | 
|  | #include "components/user_manager/user_manager.h" | 
|  | #include "components/user_manager/user_type.h" | 
|  |  | 
|  | namespace ash::system { | 
|  |  | 
|  | SystemClock::SystemClock() { | 
|  | device_settings_observer_ = CrosSettings::Get()->AddSettingsObserver( | 
|  | kSystemUse24HourClock, | 
|  | base::BindRepeating(&SystemClock::OnSystemPrefChanged, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  |  | 
|  | user_manager::UserManager::Get()->AddSessionStateObserver(this); | 
|  | } | 
|  |  | 
|  | SystemClock::~SystemClock() { | 
|  | if (user_manager::UserManager::IsInitialized()) | 
|  | user_manager::UserManager::Get()->RemoveSessionStateObserver(this); | 
|  | } | 
|  |  | 
|  | void SystemClock::OnProfileWillBeDestroyed(Profile* profile) { | 
|  | DCHECK(profile_observation_.IsObservingSource(profile)); | 
|  | profile_observation_.Reset(); | 
|  | user_pref_registrar_.reset(); | 
|  | } | 
|  |  | 
|  | void SystemClock::ActiveUserChanged(user_manager::User* active_user) { | 
|  | if (!active_user) | 
|  | return; | 
|  |  | 
|  | active_user->AddProfileCreatedObserver( | 
|  | base::BindOnce(&SystemClock::SetProfileByUser, | 
|  | weak_ptr_factory_.GetWeakPtr(), active_user)); | 
|  | } | 
|  |  | 
|  | void SystemClock::AddObserver(SystemClockObserver* observer) { | 
|  | observer_list_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | void SystemClock::RemoveObserver(SystemClockObserver* observer) { | 
|  | observer_list_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | void SystemClock::SetProfileByUser(const user_manager::User* user) { | 
|  | SetProfile(ProfileHelper::Get()->GetProfileByUser(user)); | 
|  | } | 
|  |  | 
|  | void SystemClock::SetProfile(Profile* profile) { | 
|  | profile_observation_.Reset(); | 
|  | profile_observation_.Observe(profile); | 
|  | PrefService* prefs = profile->GetPrefs(); | 
|  | user_pref_registrar_ = std::make_unique<PrefChangeRegistrar>(); | 
|  | user_pref_registrar_->Init(prefs); | 
|  | user_pref_registrar_->Add(prefs::kUse24HourClock, | 
|  | base::BindRepeating(&SystemClock::OnUserPrefChanged, | 
|  | base::Unretained(this))); | 
|  | NotifySystemClockTypeChanged(); | 
|  | } | 
|  |  | 
|  | SystemClock::ScopedHourClockType::ScopedHourClockType( | 
|  | base::WeakPtr<SystemClock> system_clock) | 
|  | : system_clock_(std::move(system_clock)) {} | 
|  |  | 
|  | SystemClock::ScopedHourClockType::~ScopedHourClockType() { | 
|  | if (!system_clock_) | 
|  | return; | 
|  | system_clock_->scoped_hour_clock_type_.reset(); | 
|  | system_clock_->NotifySystemClockTypeChanged(); | 
|  | } | 
|  |  | 
|  | SystemClock::ScopedHourClockType::ScopedHourClockType( | 
|  | ScopedHourClockType&& other) = default; | 
|  |  | 
|  | SystemClock::ScopedHourClockType& SystemClock::ScopedHourClockType::operator=( | 
|  | ScopedHourClockType&& other) = default; | 
|  |  | 
|  | void SystemClock::ScopedHourClockType::UpdateClockType( | 
|  | base::HourClockType clock_type) { | 
|  | if (!system_clock_) | 
|  | return; | 
|  | system_clock_->scoped_hour_clock_type_ = clock_type; | 
|  | system_clock_->NotifySystemClockTypeChanged(); | 
|  | } | 
|  |  | 
|  | SystemClock::ScopedHourClockType SystemClock::CreateScopedHourClockType( | 
|  | base::HourClockType hour_clock_type) { | 
|  | DCHECK(!scoped_hour_clock_type_.has_value()); | 
|  | scoped_hour_clock_type_ = hour_clock_type; | 
|  | NotifySystemClockTypeChanged(); | 
|  | return ScopedHourClockType(weak_ptr_factory_.GetWeakPtr()); | 
|  | } | 
|  |  | 
|  | bool SystemClock::ShouldUse24HourClockForUser(const AccountId& user_id) const { | 
|  | // System value is used for kUse24HourClock preference on login screen and | 
|  | // whenever set so in user's preference | 
|  | const CrosSettings* const cros_settings = CrosSettings::Get(); | 
|  | bool system_use_24_hour_clock = true; | 
|  | const bool system_value_found = cros_settings->GetBoolean( | 
|  | kSystemUse24HourClock, &system_use_24_hour_clock); | 
|  | const bool system_value = | 
|  | system_value_found ? system_use_24_hour_clock | 
|  | : (base::GetHourClockType() == base::k24HourClock); | 
|  |  | 
|  | const user_manager::User* const user = | 
|  | user_manager::UserManager::Get()->FindUser(user_id); | 
|  | if (!user || !user->GetProfilePrefs()) { | 
|  | return system_value; | 
|  | } | 
|  |  | 
|  | const PrefService::Preference* user_pref = | 
|  | user->GetProfilePrefs()->FindPreference(prefs::kUse24HourClock); | 
|  | if (user_pref->IsDefaultValue()) { | 
|  | // Non-regular users or owner use `system_value`. Owner uses `system_value` | 
|  | // to mitigate the edge case where owner data is lost because `system_value` | 
|  | // should always be consistent with the (lost) owner data. | 
|  | if (user->GetType() != user_manager::UserType::kRegular || | 
|  | user_manager::UserManager::Get()->IsOwnerUser(user)) { | 
|  | return system_value; | 
|  | } | 
|  |  | 
|  | // Regular users use `system_value` on managed devices where `system_value` | 
|  | // is from device policy. | 
|  | DCHECK_EQ(user->GetType(), user_manager::UserType::kRegular); | 
|  | if (ash::InstallAttributes::Get()->IsEnterpriseManaged()) { | 
|  | return system_value; | 
|  | } | 
|  | } | 
|  |  | 
|  | return user_pref->GetValue()->GetIfBool().value_or(true); | 
|  | } | 
|  |  | 
|  | bool SystemClock::ShouldUse24HourClock() const { | 
|  | if (scoped_hour_clock_type_.has_value()) { | 
|  | return scoped_hour_clock_type_ == base::k24HourClock; | 
|  | } | 
|  |  | 
|  | user_manager::User* const active_user = | 
|  | user_manager::UserManager::Get()->GetActiveUser(); | 
|  |  | 
|  | return ShouldUse24HourClockForUser(active_user ? active_user->GetAccountId() | 
|  | : EmptyAccountId()); | 
|  | } | 
|  |  | 
|  | void SystemClock::OnSystemPrefChanged() { | 
|  | NotifySystemClockTypeChanged(); | 
|  | } | 
|  |  | 
|  | void SystemClock::OnUserPrefChanged() { | 
|  | // It apparently sometimes takes a while after login before the current user | 
|  | // is recognized as the owner. Make sure that the system-wide clock setting | 
|  | // is updated when the recognition eventually happens (crbug.com/278601). | 
|  | // This also works for enterprise-managed devices because they never have | 
|  | // a local owner. | 
|  | const AccountId* user_id = | 
|  | ash::AnnotatedAccountId::Get(profile_observation_.GetSource()); | 
|  | if (user_id) { | 
|  | user_manager::UserManager::Get()->GetOwnerAccountIdAsync( | 
|  | base::BindOnce(&SystemClock::MaybeCopyToOwnerSettings, | 
|  | weak_ptr_factory_.GetWeakPtr(), *user_id)); | 
|  | } | 
|  |  | 
|  | NotifySystemClockTypeChanged(); | 
|  | } | 
|  |  | 
|  | void SystemClock::MaybeCopyToOwnerSettings(const AccountId& updating_user_id, | 
|  | const AccountId& owner_id) { | 
|  | if (updating_user_id != owner_id) { | 
|  | // Non owner user. | 
|  | return; | 
|  | } | 
|  |  | 
|  | const bool use_24_hour_clock = ShouldUse24HourClockForUser(updating_user_id); | 
|  |  | 
|  | const user_manager::User* const user = | 
|  | user_manager::UserManager::Get()->FindUser(updating_user_id); | 
|  | if (!user) { | 
|  | return;  // May occur if not running on a device. | 
|  | } | 
|  | Profile* const profile = ProfileHelper::Get()->GetProfileByUser(user); | 
|  | if (!profile) { | 
|  | return;  // May occur in tests or if not running on a device. | 
|  | } | 
|  | OwnerSettingsServiceAsh* const service = | 
|  | OwnerSettingsServiceAshFactory::GetForBrowserContext(profile); | 
|  | CHECK(service); | 
|  | service->SetBoolean(kSystemUse24HourClock, use_24_hour_clock); | 
|  | } | 
|  |  | 
|  | void SystemClock::NotifySystemClockTypeChanged() { | 
|  | for (auto& observer : observer_list_) | 
|  | observer.OnSystemClockChanged(this); | 
|  | } | 
|  |  | 
|  | }  // namespace ash::system |