Commit 36d173b6 authored by David Dorwin's avatar David Dorwin Committed by Commit Bot

[fuchsia] Use fuchsia.intl.PropertyProvider to get time zone

Adds Fuchsia support to InitializeIcuTimeZone() and implements
TimeZoneMonitorFuchsia.

This causes Chrome's internal time zone to match the system.
Note: deprecatedtimezone is still used for exploded time and will be
replaced in a separate CL.

Bug: 985946
Test: JavaScript `Date()` displays the correct local time and zone.
Change-Id: I3ab495ac277f5a53f60a3f51e8b128a12370a99b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1877809
Commit-Queue: David Dorwin <ddorwin@chromium.org>
Reviewed-by: default avatarYuchen Liu <yucliu@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@google.com>
Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721862}
parent ab468693
......@@ -1544,6 +1544,8 @@ jumbo_component("base") {
"fuchsia/filtered_service_directory.h",
"fuchsia/fuchsia_logging.cc",
"fuchsia/fuchsia_logging.h",
"fuchsia/intl_profile_watcher.cc",
"fuchsia/intl_profile_watcher.h",
"fuchsia/scoped_service_binding.cc",
"fuchsia/scoped_service_binding.h",
"fuchsia/service_directory.cc",
......@@ -1615,6 +1617,7 @@ jumbo_component("base") {
"//third_party/fuchsia-sdk/sdk:async-loop-default",
"//third_party/fuchsia-sdk/sdk:fidl",
"//third_party/fuchsia-sdk/sdk:fuchsia-deprecatedtimezone",
"//third_party/fuchsia-sdk/sdk:fuchsia-intl",
"//third_party/fuchsia-sdk/sdk:fuchsia-sys",
"//third_party/fuchsia-sdk/sdk:syslog",
"//third_party/fuchsia-sdk/sdk:vfs_cpp",
......@@ -3039,6 +3042,7 @@ test("base_unittests") {
"files/file_descriptor_watcher_posix_unittest.cc",
"fuchsia/file_utils_unittest.cc",
"fuchsia/filtered_service_directory_unittest.cc",
"fuchsia/intl_profile_watcher_unittest.cc",
"fuchsia/scoped_service_binding_unittest.cc",
"fuchsia/service_directory_test_base.cc",
"fuchsia/service_directory_test_base.h",
......@@ -3057,6 +3061,7 @@ test("base_unittests") {
"//third_party/fuchsia-sdk/sdk:async",
"//third_party/fuchsia-sdk/sdk:async-default",
"//third_party/fuchsia-sdk/sdk:fdio",
"//third_party/fuchsia-sdk/sdk:fuchsia-intl",
"//third_party/fuchsia-sdk/sdk:fuchsia-logger",
"//third_party/fuchsia-sdk/sdk:fuchsia-sys",
"//third_party/fuchsia-sdk/sdk:sys_cpp",
......
// Copyright 2019 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 "base/fuchsia/intl_profile_watcher.h"
#include <fuchsia/intl/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <string>
#include <vector>
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/strings/string_piece.h"
using ::fuchsia::intl::Profile;
namespace base {
namespace fuchsia {
IntlProfileWatcher::IntlProfileWatcher(ProfileChangeCallback on_profile_changed)
: IntlProfileWatcher(ComponentContextForCurrentProcess()
->svc()
->Connect<::fuchsia::intl::PropertyProvider>(),
on_profile_changed) {}
IntlProfileWatcher::IntlProfileWatcher(
::fuchsia::intl::PropertyProviderPtr property_provider,
ProfileChangeCallback on_profile_changed)
: property_provider_(std::move(property_provider)),
on_profile_changed_(std::move(on_profile_changed)) {
DCHECK(property_provider_.is_bound());
DCHECK(on_profile_changed_);
property_provider_.set_error_handler([](zx_status_t status) {
ZX_LOG(ERROR, status) << "intl.PropertyProvider disconnected. "
<< "Profile changes will not be monitored.";
});
property_provider_.events().OnChange = [this]() {
property_provider_->GetProfile(
[this](Profile profile) { on_profile_changed_.Run(profile); });
};
}
IntlProfileWatcher::~IntlProfileWatcher() = default;
// static
std::string IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
const Profile& profile) {
if (!profile.has_time_zones()) {
DLOG(WARNING) << "Profile does not contain time zones.";
return std::string();
}
const std::vector<::fuchsia::intl::TimeZoneId>& time_zones =
profile.time_zones();
if (time_zones.size() == 0) {
DLOG(WARNING) << "Profile contains an empty time zones list.";
return std::string();
}
return time_zones[0].id;
}
// static
std::string IntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization() {
::fuchsia::intl::PropertyProviderSyncPtr provider;
ComponentContextForCurrentProcess()->svc()->Connect(provider.NewRequest());
return GetPrimaryTimeZoneIdFromPropertyProvider(std::move(provider));
}
// static
std::string IntlProfileWatcher::GetPrimaryTimeZoneIdFromPropertyProvider(
::fuchsia::intl::PropertyProviderSyncPtr property_provider) {
DCHECK(property_provider.is_bound());
Profile profile;
zx_status_t status = property_provider->GetProfile(&profile);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "Failed to get intl Profile";
return std::string();
}
return GetPrimaryTimeZoneIdFromProfile(profile);
}
} // namespace fuchsia
} // namespace base
// Copyright 2019 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.
#ifndef BASE_FUCHSIA_INTL_PROFILE_WATCHER_H_
#define BASE_FUCHSIA_INTL_PROFILE_WATCHER_H_
#include <fuchsia/intl/cpp/fidl.h>
#include <string>
#include "base/base_export.h"
#include "base/callback.h"
#include "base/strings/string_piece_forward.h"
namespace base {
namespace fuchsia {
// Watches fuchsia.intl.PropertyProvider for change notifications and notifies
// the provided callback. If necessary, the caller is responsible for
// determining whether an actual change of interest has occurred.
class BASE_EXPORT IntlProfileWatcher {
public:
using ProfileChangeCallback =
base::RepeatingCallback<void(const ::fuchsia::intl::Profile&)>;
// |on_profile_changed| will be called each time the profile may have changed.
explicit IntlProfileWatcher(ProfileChangeCallback on_profile_changed);
IntlProfileWatcher(const IntlProfileWatcher&) = delete;
IntlProfileWatcher& operator=(const IntlProfileWatcher&) = delete;
~IntlProfileWatcher();
// Returns the ID of the primary time zone in |profile|.
// Returns the empty string if the ID cannot be obtained.
static std::string GetPrimaryTimeZoneIdFromProfile(
const ::fuchsia::intl::Profile& profile);
// Returns the ID of the primary time zone for the system.
// Returns the empty string if the ID cannot be obtained.
// This is a synchronous blocking call to the system service and should only
// be used for ICU initialization.
static std::string GetPrimaryTimeZoneIdForIcuInitialization();
private:
friend class GetPrimaryTimeZoneIdFromPropertyProviderTest;
friend class IntlProfileWatcherTest;
IntlProfileWatcher(::fuchsia::intl::PropertyProviderPtr property_provider,
ProfileChangeCallback on_profile_changed);
// Returns the ID of the primary time zone from the profile obtained from
// |property_provider|. Returns the empty string if the ID cannot be obtained.
static std::string GetPrimaryTimeZoneIdFromPropertyProvider(
::fuchsia::intl::PropertyProviderSyncPtr property_provider);
::fuchsia::intl::PropertyProviderPtr property_provider_;
const ProfileChangeCallback on_profile_changed_;
};
} // namespace fuchsia
} // namespace base
#endif // BASE_FUCHSIA_INTL_PROFILE_WATCHER_H_
// Copyright 2019 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 "base/fuchsia/intl_profile_watcher.h"
#include <fuchsia/intl/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h>
#include <memory>
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::fuchsia::intl::Profile;
namespace base {
namespace fuchsia {
namespace {
const char kPrimaryTimeZoneName[] = "Australia/Darwin";
const char kSecondaryTimeZoneName[] = "Africa/Djibouti";
void CopyIdsToTimeZoneIds(
const std::vector<std::string>& zone_ids,
std::vector<::fuchsia::intl::TimeZoneId>* time_zone_ids) {
time_zone_ids->clear();
for (auto id : zone_ids) {
::fuchsia::intl::TimeZoneId time_zone_id;
time_zone_id.id = id;
time_zone_ids->push_back(time_zone_id);
}
}
Profile CreateProfileWithTimeZones(const std::vector<std::string>& zone_ids) {
Profile profile;
std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids;
CopyIdsToTimeZoneIds(zone_ids, &time_zone_ids);
profile.set_time_zones(time_zone_ids);
return profile;
}
// Partial fake implementation of a PropertyProvider.
class FakePropertyProvider
: public ::fuchsia::intl::testing::PropertyProvider_TestBase {
public:
explicit FakePropertyProvider(
fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
provider_request)
: binding_(this) {
binding_.Bind(std::move(provider_request));
DCHECK(binding_.is_bound());
}
FakePropertyProvider(const FakePropertyProvider&) = delete;
FakePropertyProvider& operator=(const FakePropertyProvider&) = delete;
~FakePropertyProvider() override = default;
void Close() { binding_.Close(ZX_ERR_PEER_CLOSED); }
void SetTimeZones(const std::vector<std::string>& zone_ids) {
CopyIdsToTimeZoneIds(zone_ids, &time_zone_ids_);
}
void NotifyChange() { binding_.events().OnChange(); }
// PropertyProvider_TestBase implementation.
void GetProfile(
::fuchsia::intl::PropertyProvider::GetProfileCallback callback) override {
Profile profile;
profile.set_time_zones(time_zone_ids_);
callback(std::move(profile));
}
void NotImplemented_(const std::string& name) override {
ADD_FAILURE() << "Unimplemented function called: " << name;
}
private:
::fidl::Binding<::fuchsia::intl::PropertyProvider> binding_;
std::vector<::fuchsia::intl::TimeZoneId> time_zone_ids_;
};
class FakePropertyProviderAsync {
public:
explicit FakePropertyProviderAsync(
fidl::InterfaceRequest<::fuchsia::intl::PropertyProvider>
provider_request)
: thread_("Property Provider Thread") {
base::Thread::Options options(base::MessagePumpType::IO, 0);
CHECK(thread_.StartWithOptions(options));
property_provider_ = base::SequenceBound<FakePropertyProvider>(
thread_.task_runner(), std::move(provider_request));
}
FakePropertyProviderAsync(const FakePropertyProviderAsync&) = delete;
FakePropertyProviderAsync& operator=(const FakePropertyProviderAsync&) =
delete;
~FakePropertyProviderAsync() = default;
void Close() {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::Close);
}
void SetTimeZones(const std::vector<std::string>& zone_ids) {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::SetTimeZones,
zone_ids);
}
void NotifyChange() {
property_provider_.Post(FROM_HERE, &FakePropertyProvider::NotifyChange);
}
private:
base::Thread thread_;
base::SequenceBound<FakePropertyProvider> property_provider_;
};
} // namespace
class GetPrimaryTimeZoneIdFromPropertyProviderTest : public testing::Test {
public:
GetPrimaryTimeZoneIdFromPropertyProviderTest()
: property_provider_(property_provider_ptr_.NewRequest()) {}
GetPrimaryTimeZoneIdFromPropertyProviderTest(
const GetPrimaryTimeZoneIdFromPropertyProviderTest&) = delete;
GetPrimaryTimeZoneIdFromPropertyProviderTest& operator=(
const GetPrimaryTimeZoneIdFromPropertyProviderTest&) = delete;
~GetPrimaryTimeZoneIdFromPropertyProviderTest() override = default;
protected:
static std::string GetPrimaryTimeZoneIdFromPropertyProvider(
::fuchsia::intl::PropertyProviderSyncPtr property_provider) {
return IntlProfileWatcher::GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider));
}
::fuchsia::intl::PropertyProviderSyncPtr property_provider_ptr_;
FakePropertyProviderAsync property_provider_;
};
class IntlProfileWatcherTest : public testing::Test {
public:
IntlProfileWatcherTest()
: property_provider_(property_provider_ptr_.NewRequest()) {}
IntlProfileWatcherTest(const IntlProfileWatcherTest&) = delete;
IntlProfileWatcherTest& operator=(const IntlProfileWatcherTest&) = delete;
~IntlProfileWatcherTest() override = default;
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
std::unique_ptr<IntlProfileWatcher> CreateIntlProfileWatcher(
IntlProfileWatcher::ProfileChangeCallback on_profile_changed) {
return base::WrapUnique(new IntlProfileWatcher(
std::move(property_provider_ptr_), std::move(on_profile_changed)));
}
::fuchsia::intl::PropertyProviderPtr property_provider_ptr_;
FakePropertyProviderAsync property_provider_;
base::RunLoop run_loop_;
};
// Unit tests are run in an environment where intl is not provided, so the FIDL
// calls always fail.
TEST(IntlServiceNotAvailableTest, GetPrimaryTimeZoneIdForIcuInitialization) {
EXPECT_STREQ(
"",
IntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization().c_str());
}
// Unit tests are run in an environment where intl is not provided.
// However, this is not exposed by the API.
TEST(IntlServiceNotAvailableTest, IntlProfileWatcher) {
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
base::RunLoop run_loop;
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback>
on_profile_changed;
EXPECT_CALL(on_profile_changed, Run(testing::_)).Times(0);
auto watcher = std::make_unique<IntlProfileWatcher>(on_profile_changed.Get());
EXPECT_TRUE(watcher);
run_loop.RunUntilIdle();
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, RemoteNotBound) {
// Simulate the service not actually being available.
property_provider_.Close();
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, NoZones) {
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, SingleZone) {
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, SingleZoneIsEmpty) {
property_provider_.SetTimeZones({""});
EXPECT_STREQ("", GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(GetPrimaryTimeZoneIdFromPropertyProviderTest, MoreThanOneZone) {
property_provider_.SetTimeZones(
{kPrimaryTimeZoneName, kSecondaryTimeZoneName});
EXPECT_STREQ(kPrimaryTimeZoneName, GetPrimaryTimeZoneIdFromPropertyProvider(
std::move(property_provider_ptr_))
.c_str());
}
TEST_F(IntlProfileWatcherTest, NoZones_NoNotification) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
auto watcher = CreateIntlProfileWatcher(callback.Get());
run_loop_.RunUntilIdle();
}
TEST_F(IntlProfileWatcherTest, ChangeNotification_AfterInitialization) {
auto watcher = CreateIntlProfileWatcher(base::BindLambdaForTesting(
[quit_loop = run_loop_.QuitClosure()](const Profile& profile) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(profile));
quit_loop.Run();
}));
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
property_provider_.NotifyChange();
run_loop_.Run();
}
TEST_F(IntlProfileWatcherTest, ChangeNotification_BeforeInitialization) {
property_provider_.SetTimeZones({kPrimaryTimeZoneName});
property_provider_.NotifyChange();
auto watcher = CreateIntlProfileWatcher(base::BindLambdaForTesting(
[quit_loop = run_loop_.QuitClosure()](const Profile& profile) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(profile));
quit_loop.Run();
}));
run_loop_.Run();
}
// Ensure no crash when the peer service cannot be reached during creation.
TEST_F(IntlProfileWatcherTest, ChannelClosedBeforeCreation) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
property_provider_.Close();
auto watcher = CreateIntlProfileWatcher(callback.Get());
property_provider_.NotifyChange();
run_loop_.RunUntilIdle();
}
// Ensure no crash when the channel is closed after creation.
TEST_F(IntlProfileWatcherTest, ChannelClosedAfterCreation) {
base::MockCallback<IntlProfileWatcher::ProfileChangeCallback> callback;
EXPECT_CALL(callback, Run(testing::_)).Times(0);
auto watcher = CreateIntlProfileWatcher(callback.Get());
property_provider_.Close();
property_provider_.NotifyChange();
run_loop_.RunUntilIdle();
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, NoZones) {
EXPECT_EQ("", IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(Profile()));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, EmptyZonesList) {
EXPECT_EQ("", IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones({})));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, OneZone) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones({kPrimaryTimeZoneName})));
}
TEST(IntlProfileWatcherGetPrimaryTimeZoneIdFromProfileTest, TwoZones) {
EXPECT_EQ(kPrimaryTimeZoneName,
IntlProfileWatcher::GetPrimaryTimeZoneIdFromProfile(
CreateProfileWithTimeZones(
{kPrimaryTimeZoneName, kSecondaryTimeZoneName})));
}
} // namespace fuchsia
} // namespace base
......@@ -34,7 +34,16 @@
#include "base/mac/foundation_util.h"
#endif
#if defined(OS_ANDROID) || (defined(OS_LINUX) && !defined(IS_CHROMECAST))
#if defined(OS_FUCHSIA)
#include "base/fuchsia/intl_profile_watcher.h"
#endif
#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
#include "third_party/icu/source/common/unicode/unistr.h"
#endif
#if defined(OS_ANDROID) || defined(OS_FUCHSIA) || \
(defined(OS_LINUX) && !defined(IS_CHROMECAST))
#include "third_party/icu/source/i18n/unicode/timezone.h"
#endif
......@@ -266,6 +275,19 @@ void InitializeIcuTimeZone() {
string16 zone_id = android::GetDefaultTimeZoneId();
icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
icu::UnicodeString(FALSE, zone_id.data(), zone_id.length())));
#elif defined(OS_FUCHSIA)
// The platform-specific mechanisms used by ICU's detectHostTimeZone() to
// determine the default timezone will not work on Fuchsia. Therefore,
// proactively set the default system.
// This is also required by TimeZoneMonitorFuchsia::ProfileMayHaveChanged(),
// which uses the current default to detect whether the time zone changed in
// the new profile.
// If the system time zone cannot be obtained or is not understood by ICU,
// the "unknown" time zone will be returned by createTimeZone() and used.
std::string zone_id =
fuchsia::IntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization();
icu::TimeZone::adoptDefault(
icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(zone_id)));
#elif defined(OS_LINUX) && !defined(IS_CHROMECAST)
// To respond to the timezone change properly, the default timezone
// cache in ICU has to be populated on starting up.
......
......@@ -16,6 +16,7 @@
"fuchsia.device.NameProvider",
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.Log",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
......
......@@ -15,6 +15,7 @@
"fuchsia.device.NameProvider",
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.Log",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
......
......@@ -18,6 +18,7 @@
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.media.Audio",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.net.NameLookup",
......
......@@ -9,6 +9,7 @@
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.media.drm.Widevine",
......
......@@ -10,6 +10,7 @@
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.net.NameLookup",
......
......@@ -7,6 +7,7 @@
"services": [
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.net.NameLookup",
"fuchsia.netstack.Netstack",
......
......@@ -6,6 +6,7 @@
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.media.drm.PlayReady",
......
......@@ -8,6 +8,7 @@
"fuchsia.deprecatedtimezone.Timezone",
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.media.Audio",
"fuchsia.media.drm.Widevine",
......
......@@ -6,19 +6,48 @@
#include <memory>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/intl_profile_watcher.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "third_party/icu/source/common/unicode/unistr.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
namespace device {
namespace {
// TODO(fuchsia): Implement this. crbug.com/750934
class TimeZoneMonitorFuchsia : public TimeZoneMonitor {
public:
TimeZoneMonitorFuchsia() = default;
TimeZoneMonitorFuchsia()
: watcher_(base::BindRepeating(&TimeZoneMonitorFuchsia::OnProfileChanged,
base::Unretained(this))) {}
TimeZoneMonitorFuchsia(const TimeZoneMonitorFuchsia&) = delete;
TimeZoneMonitorFuchsia& operator=(const TimeZoneMonitorFuchsia&) = delete;
~TimeZoneMonitorFuchsia() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(TimeZoneMonitorFuchsia);
void OnProfileChanged(const ::fuchsia::intl::Profile& profile) {
std::string new_zone_id = watcher_.GetPrimaryTimeZoneIdFromProfile(profile);
std::unique_ptr<icu::TimeZone> new_zone(
base::WrapUnique(icu::TimeZone::createTimeZone(
icu::UnicodeString::fromUTF8(new_zone_id))));
// Changes to profile properties other than the time zone may have caused
// the notification, but we only want to update the ICU default zone and
// notify renderers if the time zone changed. The timezone must have
// previously been populated. See InitializeICU().
std::unique_ptr<icu::TimeZone> current_zone(icu::TimeZone::createDefault());
if (*current_zone == *new_zone) {
DVLOG(1) << "timezone already updated";
return;
}
UpdateIcuAndNotifyClients(std::move(new_zone));
}
base::fuchsia::IntlProfileWatcher watcher_;
};
} // namespace
......@@ -26,8 +55,6 @@ class TimeZoneMonitorFuchsia : public TimeZoneMonitor {
// static
std::unique_ptr<TimeZoneMonitor> TimeZoneMonitor::Create(
scoped_refptr<base::SequencedTaskRunner> file_task_runner) {
// TODO(https://crbug.com/750934): Implement a real TimeZoneMonitor.
return std::make_unique<TimeZoneMonitorFuchsia>();
}
......
......@@ -112,6 +112,7 @@ component("sandbox") {
deps += [
"//third_party/fuchsia-sdk/sdk:fuchsia-deprecatedtimezone",
"//third_party/fuchsia-sdk/sdk:fuchsia-fonts",
"//third_party/fuchsia-sdk/sdk:fuchsia-intl",
"//third_party/fuchsia-sdk/sdk:fuchsia-logger",
"//third_party/fuchsia-sdk/sdk:fuchsia-mediacodec",
"//third_party/fuchsia-sdk/sdk:fuchsia-net",
......
......@@ -10,6 +10,7 @@
#include <fuchsia/deprecatedtimezone/cpp/fidl.h>
#include <fuchsia/fonts/cpp/fidl.h>
#include <fuchsia/intl/cpp/fidl.h>
#include <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <fuchsia/net/cpp/fidl.h>
......@@ -119,6 +120,7 @@ const SandboxConfig& GetConfigForSandboxType(SandboxType type) {
// Services that are passed to all processes.
constexpr base::span<const char* const> kDefaultServices = base::make_span(
(const char* const[]){fuchsia::deprecatedtimezone::Timezone::Name_,
fuchsia::intl::PropertyProvider::Name_,
fuchsia::logger::LogSink::Name_});
} // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment