Skip to content

Commit

Permalink
Add GeoLanguageProvider interface.
Browse files Browse the repository at this point in the history
Original author: amoylan@chromium.org
Original cl: https://chromium-review.googlesource.com/c/chromium/src/+/706439

This CL adds the GeoLanguageProvider browser singleton as described in
the design doc:
https://docs.google.com/document/d/18WqVHz5F9vaUiE32E8Ge6QHmku2QSJKvlqB9JjnIM-g

This interface provides a list of language codes corresponding to local
languages based on the device's approximate geolocation derived from
its public IP address, using the PublicIpAddressGeolocator service.
IP geolocation updates are requested no more often than every 24 hours.

This singleton is started up after Profile init in
PreMainMessageLoopRun. The startup runs on a background task runner,
connecting & subscribing to the IP geolocation service. Startup is gated
on the Feature setting "GeoLanguage" which is false for now.

Bug: 76915
Change-Id: I9af2b1375fabda3d029ca396043b9195acab7cf0
Reviewed-on: https://chromium-review.googlesource.com/807884
Commit-Queue: Renjie Liu <renjieliu@chromium.org>
Reviewed-by: Miguel Casas <mcasas@chromium.org>
Reviewed-by: Zhongyi Shi <zhongyi@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523709}
  • Loading branch information
Renjie Liu authored and Commit Bot committed Dec 13, 2017
1 parent e48a05c commit 1aea053
Show file tree
Hide file tree
Showing 6 changed files with 475 additions and 1 deletion.
1 change: 1 addition & 0 deletions chrome/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,7 @@ split_static_library("browser") {
"//components/infobars/core",
"//components/invalidation/impl",
"//components/keyed_service/content",
"//components/language/content/browser",
"//components/language/core/browser",
"//components/metrics:call_stacks",
"//components/metrics:component_metrics",
Expand Down
20 changes: 20 additions & 0 deletions components/language/content/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,34 @@ source_set("language_code_locator") {
]
}

static_library("browser") {
sources = [
"geo_language_provider.cc",
"geo_language_provider.h",
]

deps = [
":language_code_locator",
"//base",
"//net",
"//services/device/public/interfaces:interfaces",
"//services/service_manager/public/cpp",
]
}

source_set("unit_tests") {
testonly = true
sources = [
"geo_language_provider_unittest.cc",
"language_code_locator_unittest.cc",
]
deps = [
":browser",
":language_code_locator",
"//base",
"//base/test:test_support",
"//services/device/public/interfaces:interfaces",
"//services/service_manager/public/cpp",
"//testing/gmock",
"//testing/gtest",
]
Expand Down
5 changes: 4 additions & 1 deletion components/language/content/browser/DEPS
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
include_rules = [
"+content/public/browser",
"+device/geolocation/public/interfaces",
"+net",
"+services/device/public/interfaces",
"+services/service_manager/public",
]
153 changes: 153 additions & 0 deletions components/language/content/browser/geo_language_provider.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2017 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 "components/language/content/browser/geo_language_provider.h"

#include "base/memory/singleton.h"
#include "base/task_scheduler/post_task.h"
#include "base/time/time.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/device/public/interfaces/constants.mojom.h"
#include "services/device/public/interfaces/public_ip_address_geolocation_provider.mojom.h"
#include "services/service_manager/public/cpp/connector.h"

namespace language {
namespace {

// Don't start requesting updates to IP-based approximation geolocation until
// this long after receiving the last one.
constexpr base::TimeDelta kMinUpdatePeriod = base::TimeDelta::FromDays(1);

} // namespace

GeoLanguageProvider::GeoLanguageProvider()
: languages_(),
creation_task_runner_(base::SequencedTaskRunnerHandle::Get()),
background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
// Constructor is not required to run on |background_task_runner_|:
DETACH_FROM_SEQUENCE(background_sequence_checker_);
}

GeoLanguageProvider::GeoLanguageProvider(
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: languages_(),
creation_task_runner_(base::SequencedTaskRunnerHandle::Get()),
background_task_runner_(background_task_runner) {
// Constructor is not required to run on |background_task_runner_|:
DETACH_FROM_SEQUENCE(background_sequence_checker_);
}

GeoLanguageProvider::~GeoLanguageProvider() = default;

/* static */
GeoLanguageProvider* GeoLanguageProvider::GetInstance() {
return base::Singleton<GeoLanguageProvider, base::LeakySingletonTraits<
GeoLanguageProvider>>::get();
}

void GeoLanguageProvider::StartUp(
std::unique_ptr<service_manager::Connector> service_manager_connector) {
DCHECK_CALLED_ON_VALID_SEQUENCE(creation_sequence_checker_);

service_manager_connector_ = std::move(service_manager_connector);
// Continue startup in the background.
background_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&GeoLanguageProvider::BackgroundStartUp,
base::Unretained(this)));
}

std::vector<std::string> GeoLanguageProvider::CurrentGeoLanguages() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(creation_sequence_checker_);
return languages_;
}

void GeoLanguageProvider::BackgroundStartUp() {
// This binds background_sequence_checker_.
DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);

// Initialize location->language lookup library.
language_code_locator_ = std::make_unique<language::LanguageCodeLocator>();

// Make initial query.
QueryNextPosition();
}

void GeoLanguageProvider::BindIpGeolocationService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);
DCHECK(!geolocation_provider_.is_bound());

// Bind a PublicIpAddressGeolocationProvider.
device::mojom::PublicIpAddressGeolocationProviderPtr ip_geolocation_provider;
service_manager_connector_->BindInterface(
device::mojom::kServiceName, mojo::MakeRequest(&ip_geolocation_provider));

net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation("geo_language_provider",
"network_location_request",
R"(
semantics {
sender: "GeoLanguage Provider"
}
policy {
setting:
"Users can control this feature via the translation settings "
"'Languages', 'Language', 'Offer to translate'."
chrome_policy {
DefaultGeolocationSetting {
DefaultGeolocationSetting: 2
}
}
})");

// Use the PublicIpAddressGeolocationProvider to bind ip_geolocation_service_.
ip_geolocation_provider->CreateGeolocation(
static_cast<net::MutablePartialNetworkTrafficAnnotationTag>(
partial_traffic_annotation),
mojo::MakeRequest(&geolocation_provider_));
// No error handler required: If the connection is broken, QueryNextPosition
// will bind it again.
}

void GeoLanguageProvider::QueryNextPosition() {
DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);

if (geolocation_provider_.encountered_error())
geolocation_provider_.reset();
if (!geolocation_provider_.is_bound())
BindIpGeolocationService();

geolocation_provider_->QueryNextPosition(base::BindOnce(
&GeoLanguageProvider::OnIpGeolocationResponse, base::Unretained(this)));
}

void GeoLanguageProvider::OnIpGeolocationResponse(
device::mojom::GeopositionPtr geoposition) {
DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);

const std::vector<std::string> languages =
language_code_locator_->GetLanguageCode(geoposition->latitude,
geoposition->longitude);

// Update current languages on UI thread.
creation_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&GeoLanguageProvider::SetGeoLanguages,
base::Unretained(this), languages));

// Post a task to request a fresh lookup after |kMinUpdatePeriod|.
background_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&GeoLanguageProvider::QueryNextPosition,
base::Unretained(this)),
kMinUpdatePeriod);
}

void GeoLanguageProvider::SetGeoLanguages(
const std::vector<std::string>& languages) {
DCHECK_CALLED_ON_VALID_SEQUENCE(creation_sequence_checker_);
languages_ = languages;
}

} // namespace language
114 changes: 114 additions & 0 deletions components/language/content/browser/geo_language_provider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2017 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 COMPONENTS_LANGUAGE_CONTENT_BROWSER_GEO_LANGUAGE_PROVIDER_H_
#define COMPONENTS_LANGUAGE_CONTENT_BROWSER_GEO_LANGUAGE_PROVIDER_H_

#include <string>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "components/language/content/browser/language_code_locator.h"
#include "device/geolocation/public/interfaces/geolocation.mojom.h"

namespace base {
template <typename T>
struct DefaultSingletonTraits;
}

namespace service_manager {
class Connector;
}

namespace language {
// GeoLanguageProvider is responsible for providing a "local" language derived
// from the approximate geolocation of the device based only on its public IP
// address.
// * Singleton class. Access through GetInstance().
// * Sequencing: Must be created and used on the same sequence.
class GeoLanguageProvider {
public:
static GeoLanguageProvider* GetInstance();

// Call this once near browser startup. Begins ongoing geo-language updates.
// * Initializes location->language mapping in a low-priority background task.
// * Until the first IP geolocation completes, CurrentGeoLanguages() will
// return an empty list.
// |service_manager_connector| should not yet be bound to a sequence, e.g., it
// should be the result of invoking ServiceManagerConnect::Clone() on another
// connector.
void StartUp(
std::unique_ptr<service_manager::Connector> service_manager_connector);

// Returns the inferred ranked list of local languages based on the most
// recently obtained approximate public-IP geolocation of the device.
// * Returns a list of BCP-47 language codes.
// * Returns an empty list in these cases:
// - StartUp() not yet called
// - Geolocation failed
// - Geolocation pending
// - Geolocation succeeded but no local language is mapped to that location
std::vector<std::string> CurrentGeoLanguages() const;

private:
friend class GeoLanguageProviderTest;

GeoLanguageProvider();
explicit GeoLanguageProvider(
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
~GeoLanguageProvider();
friend struct base::DefaultSingletonTraits<GeoLanguageProvider>;

// Performs actual work described in StartUp() above.
void BackgroundStartUp();

// Binds |ip_geolocation_service_| using a service_manager::Connector.
void BindIpGeolocationService();

// Requests the next available IP-based approximate geolocation from
// |ip_geolocation_service_|, binding |ip_geolocation_service_| first if
// necessary.
void QueryNextPosition();

// Updates the list of BCP-47 language codes that will be returned by calls to
// CurrentGeoLanguages().
// Must be called on the UI thread.
void SetGeoLanguages(const std::vector<std::string>& languages);

// Callback for updates from |ip_geolocation_service_|.
void OnIpGeolocationResponse(device::mojom::GeopositionPtr geoposition);

// List of BCP-47 language code inferred from public-IP geolocation.
// May be empty. See comment on CurrentGeoLanguages() above.
std::vector<std::string> languages_;

// Service manager connector for use on background_task_runner_.
std::unique_ptr<service_manager::Connector> service_manager_connector_;

// Connection to the IP geolocation service.
device::mojom::GeolocationPtr geolocation_provider_;

// Location -> Language lookup library.
std::unique_ptr<language::LanguageCodeLocator> language_code_locator_;

// Runner for tasks that should run on the creation sequence.
scoped_refptr<base::SequencedTaskRunner> creation_task_runner_;

// Runner for low priority background tasks.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;

// Sequence checker for methods that must run on the creation sequence.
SEQUENCE_CHECKER(creation_sequence_checker_);

// Sequence checker for background_task_runner_.
SEQUENCE_CHECKER(background_sequence_checker_);

DISALLOW_COPY_AND_ASSIGN(GeoLanguageProvider);
};

} // namespace language

#endif // COMPONENTS_LANGUAGE_CONTENT_BROWSER_GEO_LANGUAGE_PROVIDER_H_
Loading

0 comments on commit 1aea053

Please sign in to comment.