Commit 843f6a0d authored by Josh Nohle's avatar Josh Nohle Committed by Commit Bot

[Nearby] Add local device data updater

The local device data updater builds and queues UpdateDevice RPC
requests. It uses the Nearby Share HTTP client to make the calls when
ready. It also enforces the specified timeout.

Bug: b/157685298
Change-Id: I27372ccfdb27307c5a048b4293a4eac005f61a40
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2280581Reviewed-by: default avatarJames Vecore <vecore@google.com>
Commit-Queue: Josh Nohle <nohle@chromium.org>
Cr-Commit-Position: refs/heads/master@{#789725}
parent b5120650
......@@ -3735,6 +3735,7 @@ static_library("browser") {
"//chrome/browser/nearby_sharing/certificates",
"//chrome/browser/nearby_sharing/client",
"//chrome/browser/nearby_sharing/instantmessaging/proto",
"//chrome/browser/nearby_sharing/local_device_data",
"//chrome/browser/nearby_sharing/logging",
"//chrome/browser/nearby_sharing/logging:util",
"//chrome/browser/nearby_sharing/proto",
......
# Copyright 2020 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.
source_set("local_device_data") {
sources = [
"nearby_share_device_data_updater.cc",
"nearby_share_device_data_updater.h",
"nearby_share_device_data_updater_impl.cc",
"nearby_share_device_data_updater_impl.h",
]
deps = [
"//base",
"//chrome/browser/nearby_sharing/client",
"//chrome/browser/nearby_sharing/proto",
]
}
source_set("test_support") {
testonly = true
sources = [
"fake_nearby_share_device_data_updater.cc",
"fake_nearby_share_device_data_updater.h",
]
deps = [
":local_device_data",
"//base",
"//chrome/browser/nearby_sharing/proto",
]
}
source_set("unit_tests") {
testonly = true
sources = [ "nearby_share_device_data_updater_impl_unittest.cc" ]
deps = [
":local_device_data",
"//base",
"//base/test:test_support",
"//chrome/browser/nearby_sharing/client",
"//chrome/browser/nearby_sharing/client:test_support",
"//chrome/browser/nearby_sharing/proto",
"//testing/gtest",
]
}
// Copyright 2020 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/nearby_sharing/local_device_data/fake_nearby_share_device_data_updater.h"
FakeNearbyShareDeviceDataUpdater::FakeNearbyShareDeviceDataUpdater(
const std::string& device_id)
: NearbyShareDeviceDataUpdater(device_id) {}
FakeNearbyShareDeviceDataUpdater::~FakeNearbyShareDeviceDataUpdater() = default;
void FakeNearbyShareDeviceDataUpdater::RunNextRequest(
bool success,
const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
FinishAttempt(success, response);
}
void FakeNearbyShareDeviceDataUpdater::HandleNextRequest() {}
std::unique_ptr<NearbyShareDeviceDataUpdater>
FakeNearbyShareDeviceDataUpdaterFactory::CreateInstance(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory) {
latest_timeout_ = timeout;
latest_client_factory_ = client_factory;
auto instance = std::make_unique<FakeNearbyShareDeviceDataUpdater>(device_id);
instances_.push_back(instance.get());
return instance;
}
// Copyright 2020 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 CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_FAKE_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
#define CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_FAKE_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/containers/queue.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater_impl.h"
#include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h"
// An fake implementation of NearbyShareDeviceDataUpdater for use in unit tests.
class FakeNearbyShareDeviceDataUpdater : public NearbyShareDeviceDataUpdater {
public:
explicit FakeNearbyShareDeviceDataUpdater(const std::string& device_id);
~FakeNearbyShareDeviceDataUpdater() override;
// Advances the request queue and invokes request callback with the input
// parameters |success| and |response|.
void RunNextRequest(
bool success,
const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response);
const std::string& device_id() const { return device_id_; }
const base::queue<Request>& pending_requests() const {
return pending_requests_;
}
private:
// NearbyShareDeviceDataUpdater:
void HandleNextRequest() override;
};
// A fake NearbyShareDeviceDataUpdaterImpl factory for use in unit tests.
class FakeNearbyShareDeviceDataUpdaterFactory
: public NearbyShareDeviceDataUpdaterImpl::Factory {
public:
FakeNearbyShareDeviceDataUpdaterFactory();
~FakeNearbyShareDeviceDataUpdaterFactory() override;
// Returns all FakeNearbyShareDeviceDataUpdater instances created by
// CreateInstance().
std::vector<FakeNearbyShareDeviceDataUpdater*>& instances() {
return instances_;
}
base::TimeDelta latest_timeout() const { return latest_timeout_; }
NearbyShareClientFactory* latest_client_factory() const {
return latest_client_factory_;
}
private:
// NearbyShareDeviceDataUpdaterImpl::Factory:
std::unique_ptr<NearbyShareDeviceDataUpdater> CreateInstance(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory) override;
std::vector<FakeNearbyShareDeviceDataUpdater*> instances_;
base::TimeDelta latest_timeout_;
NearbyShareClientFactory* latest_client_factory_ = nullptr;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_FAKE_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
// Copyright 2020 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 <utility>
#include "base/check.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h"
NearbyShareDeviceDataUpdater::Request::Request(
base::Optional<std::string> device_name,
base::Optional<std::vector<nearbyshare::proto::Contact>> contacts,
base::Optional<std::vector<nearbyshare::proto::PublicCertificate>>
certificates,
ResultCallback callback)
: device_name(std::move(device_name)),
contacts(std::move(contacts)),
certificates(std::move(certificates)),
callback(std::move(callback)) {}
NearbyShareDeviceDataUpdater::Request::Request(
NearbyShareDeviceDataUpdater::Request&& request) = default;
NearbyShareDeviceDataUpdater::Request&
NearbyShareDeviceDataUpdater::Request::operator=(
NearbyShareDeviceDataUpdater::Request&& request) = default;
NearbyShareDeviceDataUpdater::Request::~Request() = default;
NearbyShareDeviceDataUpdater::NearbyShareDeviceDataUpdater(
const std::string& device_id)
: device_id_(device_id) {}
NearbyShareDeviceDataUpdater::~NearbyShareDeviceDataUpdater() = default;
void NearbyShareDeviceDataUpdater::UpdateDeviceData(
base::Optional<std::string> device_name,
base::Optional<std::vector<nearbyshare::proto::Contact>> contacts,
base::Optional<std::vector<nearbyshare::proto::PublicCertificate>>
certificates,
ResultCallback callback) {
pending_requests_.emplace(std::move(device_name), std::move(contacts),
std::move(certificates), std::move(callback));
ProcessRequestQueue();
}
void NearbyShareDeviceDataUpdater::ProcessRequestQueue() {
if (is_request_in_progress_ || pending_requests_.empty())
return;
is_request_in_progress_ = true;
HandleNextRequest();
}
void NearbyShareDeviceDataUpdater::FinishAttempt(
bool success,
const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
DCHECK(is_request_in_progress_);
DCHECK(!pending_requests_.empty());
Request current_request = std::move(pending_requests_.front());
pending_requests_.pop();
std::move(current_request.callback).Run(success, response);
is_request_in_progress_ = false;
ProcessRequestQueue();
}
// Copyright 2020 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 CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
#define CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
#include <string>
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/optional.h"
#include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
// Manages a queue of data needed to make UpdateDevice RPC requests to the
// Nearby Server. Implementations should make the actual HTTP calls by
// overriding HandleNextRequest(), which is invoked when the next request is
// ready to be run. Implementations should call FinishAttempt() with the result
// of the attempt and possibly the response.
// TODO(crbug.com/1105547): Instead of queuing requests, hold a single pending
// request and update the fields as other UpdateDeviceData() call are made.
// Then, queue up all of callbacks from the merged requests in the Request
// struct; invoke the callback in the order they were added. This will reduce
// the number of UpdateDevice RPC calls.
class NearbyShareDeviceDataUpdater {
public:
using ResultCallback = base::OnceCallback<void(
bool,
const base::Optional<nearbyshare::proto::UpdateDeviceResponse>&)>;
struct Request {
Request(base::Optional<std::string> device_name,
base::Optional<std::vector<nearbyshare::proto::Contact>> contacts,
base::Optional<std::vector<nearbyshare::proto::PublicCertificate>>
certificates,
ResultCallback callback);
Request(Request&& request);
Request& operator=(Request&& request);
Request(const Request&) = delete;
Request& operator=(const Request&) = delete;
~Request();
base::Optional<std::string> device_name;
base::Optional<std::vector<nearbyshare::proto::Contact>> contacts;
base::Optional<std::vector<nearbyshare::proto::PublicCertificate>>
certificates;
ResultCallback callback;
};
// |device_id|: The ID used by the Nearby server to differentiate multiple
// devices from the same account.
explicit NearbyShareDeviceDataUpdater(const std::string& device_id);
virtual ~NearbyShareDeviceDataUpdater();
// Queue up an UpdateDevice RPC request to update the following fields on the
// Nearby server if the parameter is not base::nullopt:
//
// |device_name|: The device display name, for example, "Joe's Pixel".
//
// |contacts|: The list of contacts that the Nearby server will send
// all-contacts-visibility certificates to. Contacts marked
// is_selected will be sent selected-contacts-visibility
// certificates.
//
// |certificates|: The local device's certificates that the Nearby server will
// distribute to the appropriate |contacts|.
//
// If only the UpdateDevice RPC response data is desired, set all
// aforementioned parameters to base::nullopt.
void UpdateDeviceData(
base::Optional<std::string> device_name,
base::Optional<std::vector<nearbyshare::proto::Contact>> contacts,
base::Optional<std::vector<nearbyshare::proto::PublicCertificate>>
certificates,
ResultCallback callback);
protected:
void ProcessRequestQueue();
virtual void HandleNextRequest() = 0;
void FinishAttempt(
bool success,
const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response);
std::string device_id_;
bool is_request_in_progress_ = false;
base::queue<Request> pending_requests_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_H_
// Copyright 2020 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/nearby_sharing/local_device_data/nearby_share_device_data_updater_impl.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/nearby_sharing/client/nearby_share_client.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
namespace {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class UpdaterResultCode {
kSuccess = 0,
kTimeout = 1,
kHttpErrorOffline = 2,
kHttpErrorEndpointNotFound = 3,
kHttpErrorAuthenticationError = 4,
kHttpErrorBadRequest = 5,
kHttpErrorResponseMalformed = 6,
kHttpErrorInternalServerError = 7,
kHttpErrorUnknown = 8,
kMaxValue = kHttpErrorUnknown
};
const char kDeviceIdPrefix[] = "users/me/devices/";
const char kDeviceNameFieldMaskPath[] = "device.display_name";
const char kContactsFieldMaskPath[] = "device.contacts";
const char kCertificatesFieldMaskPath[] = "device.public_certificates";
UpdaterResultCode RequestErrorToUpdaterResultCode(
NearbyShareRequestError error) {
switch (error) {
case NearbyShareRequestError::kOffline:
return UpdaterResultCode::kHttpErrorOffline;
case NearbyShareRequestError::kEndpointNotFound:
return UpdaterResultCode::kHttpErrorEndpointNotFound;
case NearbyShareRequestError::kAuthenticationError:
return UpdaterResultCode::kHttpErrorAuthenticationError;
case NearbyShareRequestError::kBadRequest:
return UpdaterResultCode::kHttpErrorBadRequest;
case NearbyShareRequestError::kResponseMalformed:
return UpdaterResultCode::kHttpErrorResponseMalformed;
case NearbyShareRequestError::kInternalServerError:
return UpdaterResultCode::kHttpErrorInternalServerError;
case NearbyShareRequestError::kUnknown:
return UpdaterResultCode::kHttpErrorUnknown;
}
}
void RecordResultMetrics(UpdaterResultCode code) {
// TODO(crbug.com/1105579): Record a histogram value for each result.
}
} // namespace
// static
NearbyShareDeviceDataUpdaterImpl::Factory*
NearbyShareDeviceDataUpdaterImpl::Factory::test_factory_ = nullptr;
// static
std::unique_ptr<NearbyShareDeviceDataUpdater>
NearbyShareDeviceDataUpdaterImpl::Factory::Create(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory) {
if (test_factory_)
return test_factory_->CreateInstance(device_id, timeout, client_factory);
return base::WrapUnique(
new NearbyShareDeviceDataUpdaterImpl(device_id, timeout, client_factory));
}
// static
void NearbyShareDeviceDataUpdaterImpl::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
NearbyShareDeviceDataUpdaterImpl::Factory::~Factory() = default;
NearbyShareDeviceDataUpdaterImpl::NearbyShareDeviceDataUpdaterImpl(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory)
: NearbyShareDeviceDataUpdater(device_id),
timeout_(timeout),
client_factory_(client_factory) {}
NearbyShareDeviceDataUpdaterImpl::~NearbyShareDeviceDataUpdaterImpl() = default;
void NearbyShareDeviceDataUpdaterImpl::HandleNextRequest() {
timer_.Start(FROM_HERE, timeout_,
base::BindOnce(&NearbyShareDeviceDataUpdaterImpl::OnTimeout,
base::Unretained(this)));
nearbyshare::proto::UpdateDeviceRequest request;
request.mutable_device()->set_name(kDeviceIdPrefix + device_id_);
if (pending_requests_.front().device_name) {
request.mutable_device()->set_display_name(
*pending_requests_.front().device_name);
request.mutable_update_mask()->add_paths(kDeviceNameFieldMaskPath);
}
if (pending_requests_.front().contacts) {
*request.mutable_device()->mutable_contacts() = {
pending_requests_.front().contacts->begin(),
pending_requests_.front().contacts->end()};
request.mutable_update_mask()->add_paths(kContactsFieldMaskPath);
}
if (pending_requests_.front().certificates) {
*request.mutable_device()->mutable_public_certificates() = {
pending_requests_.front().certificates->begin(),
pending_requests_.front().certificates->end()};
request.mutable_update_mask()->add_paths(kCertificatesFieldMaskPath);
}
client_ = client_factory_->CreateInstance();
client_->UpdateDevice(
request,
base::BindOnce(&NearbyShareDeviceDataUpdaterImpl::OnRpcSuccess,
base::Unretained(this)),
base::BindOnce(&NearbyShareDeviceDataUpdaterImpl::OnRpcFailure,
base::Unretained(this)));
}
void NearbyShareDeviceDataUpdaterImpl::OnRpcSuccess(
const nearbyshare::proto::UpdateDeviceResponse& response) {
timer_.Stop();
nearbyshare::proto::UpdateDeviceResponse response_copy(response);
client_.reset();
RecordResultMetrics(UpdaterResultCode::kSuccess);
FinishAttempt(/*success=*/true, response_copy);
}
void NearbyShareDeviceDataUpdaterImpl::OnRpcFailure(
NearbyShareRequestError error) {
timer_.Stop();
client_.reset();
RecordResultMetrics(RequestErrorToUpdaterResultCode(error));
FinishAttempt(/*success=*/false, /*response=*/base::nullopt);
}
void NearbyShareDeviceDataUpdaterImpl::OnTimeout() {
client_.reset();
RecordResultMetrics(UpdaterResultCode::kTimeout);
FinishAttempt(/*success=*/false, /*response=*/base::nullopt);
}
// Copyright 2020 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 CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_IMPL_H_
#define CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_IMPL_H_
#include <memory>
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/nearby_sharing/client/nearby_share_request_error.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h"
#include "chrome/browser/nearby_sharing/proto/device_rpc.pb.h"
class NearbyShareClient;
class NearbyShareClientFactory;
// An implementation of NearbyShareDeviceDataUpdater that uses the
// NearbyShareClient to make UpdateDevice RPC calls to the Nearby server. An
// UpdateDeviceData() attempt will fail if a response is not received within the
// specified timeout value.
class NearbyShareDeviceDataUpdaterImpl : public NearbyShareDeviceDataUpdater {
public:
class Factory {
public:
static std::unique_ptr<NearbyShareDeviceDataUpdater> Create(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory);
static void SetFactoryForTesting(Factory* test_factory);
protected:
virtual ~Factory();
virtual std::unique_ptr<NearbyShareDeviceDataUpdater> CreateInstance(
const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory) = 0;
private:
static Factory* test_factory_;
};
~NearbyShareDeviceDataUpdaterImpl() override;
private:
NearbyShareDeviceDataUpdaterImpl(const std::string& device_id,
base::TimeDelta timeout,
NearbyShareClientFactory* client_factory);
void HandleNextRequest() override;
void OnTimeout();
void OnRpcSuccess(const nearbyshare::proto::UpdateDeviceResponse& response);
void OnRpcFailure(NearbyShareRequestError error);
base::TimeDelta timeout_;
NearbyShareClientFactory* client_factory_ = nullptr;
std::unique_ptr<NearbyShareClient> client_;
base::OneShotTimer timer_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_LOCAL_DEVICE_DATA_NEARBY_SHARE_DEVICE_DATA_UPDATER_IMPL_H_
......@@ -4341,6 +4341,7 @@ test("unit_tests") {
deps += [
"//chrome/browser/nearby_sharing/certificates:unit_tests",
"//chrome/browser/nearby_sharing/client:unit_tests",
"//chrome/browser/nearby_sharing/local_device_data:unit_tests",
"//chrome/browser/nearby_sharing/logging:unit_tests",
"//chrome/browser/nearby_sharing/proto",
"//chrome/browser/nearby_sharing/scheduling:unit_tests",
......
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