blob: d19790914b05f460cd2ffb5114ba0453c8ddab9e [file] [log] [blame]
// Copyright 2021 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/share/core/share_targets.h"
#include <string>
#include "base/check_op.h"
#include "base/files/file_util.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/share/core/share_targets_observer.h"
#include "chrome/browser/share/proto/share_target.pb.h"
#include "chrome/grit/browser_resources.h"
#include "components/country_codes/country_codes.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl.h"
#include "ui/base/resource/resource_bundle.h"
namespace sharing {
using base::AutoLock;
static const char GLOBAL[] = "GLOBAL";
// Our Singleton needs to populate itself when first constructed.
// This is left out of the constructor to make testing simpler.
struct ShareTargetsSingletonTrait
: public base::DefaultSingletonTraits<ShareTargets> {
static ShareTargets* New() {
ShareTargets* instance = new ShareTargets();
instance->PopulateFromResourceBundle();
return instance;
}
};
// --- ShareTargets methods ---
// static
ShareTargets* ShareTargets::GetInstance() {
return base::Singleton<ShareTargets, ShareTargetsSingletonTrait>::get();
}
ShareTargets::ShareTargets() = default;
ShareTargets::~ShareTargets() {
AutoLock lock(lock_); // DCHECK fail if the lock is held.
}
void ShareTargets::RecordUpdateMetrics(UpdateResult result, UpdateOrigin src) {
lock_.AssertAcquired();
// src_name should be "ResourceBundle" or "DynamicUpdate".
if (src == UpdateOrigin::DYNAMIC_UPDATE) {
UMA_HISTOGRAM_ENUMERATION("Sharing.ShareTargetUpdate.DynamicUpdateResult",
result);
if (result == UpdateResult::SUCCESS) {
UMA_HISTOGRAM_COUNTS_1000(
"Sharing.ShareTargetUpdate.DynamicUpdateVersion",
targets_->version_id());
}
} else if (src == UpdateOrigin::RESOURCE_BUNDLE) {
UMA_HISTOGRAM_ENUMERATION("Sharing.ShareTargetUpdate.ResourceBundleResult",
result);
}
}
void ShareTargets::PopulateFromDynamicUpdate(const std::string& binary_pb) {
AutoLock lock(lock_);
UpdateResult result = PopulateFromBinaryPb(binary_pb);
RecordUpdateMetrics(result, UpdateOrigin::DYNAMIC_UPDATE);
}
void ShareTargets::PopulateFromResourceBundle() {
AutoLock lock(lock_);
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
std::string binary_pb =
bundle.LoadDataResourceString(IDR_DESKTOP_SHARING_HUB_PB);
UpdateResult result = PopulateFromBinaryPb(binary_pb);
RecordUpdateMetrics(result, UpdateOrigin::RESOURCE_BUNDLE);
}
ShareTargets::UpdateResult ShareTargets::PopulateFromBinaryPb(
const std::string& binary_pb) {
lock_.AssertAcquired();
// Parse the proto and do some validation on it.
if (binary_pb.empty()) {
return UpdateResult::FAILED_EMPTY;
}
std::unique_ptr<mojom::MapLocaleTargets> new_targets(
new mojom::MapLocaleTargets);
if (!new_targets->ParseFromString(binary_pb)) {
return UpdateResult::FAILED_PROTO_PARSE;
}
// Compare against existing targets, if we have one.
if (targets_) {
// If versions are equal, we skip the update but it's not really
// a failure.
if (new_targets->version_id() == targets_->version_id())
return UpdateResult::SKIPPED_VERSION_CHECK_EQUAL;
// Check that version number increases
if (new_targets->version_id() <= targets_->version_id())
return UpdateResult::FAILED_VERSION_CHECK;
}
// Looks good. Update our internal list.
SwapTargetsLocked(new_targets);
NotifyShareTargetUpdated();
return UpdateResult::SUCCESS;
}
void ShareTargets::SwapTargetsLocked(
std::unique_ptr<mojom::MapLocaleTargets>& new_targets) {
lock_.AssertAcquired();
targets_.swap(new_targets);
}
void ShareTargets::AddObserver(ShareTargetsObserver* observer) {
observers_.AddObserver(observer);
if (targets_) {
NotifyObserver(observer);
}
}
void ShareTargets::RemoveObserver(ShareTargetsObserver* observer) {
observers_.RemoveObserver(observer);
}
std::string ShareTargets::GetCountryStringFromID(int countryID) {
// Decode the country code string from the provided integer.
unsigned char mask = 0xFF;
char c2 = static_cast<char>(mask & countryID);
char c1 = static_cast<char>(countryID >> 8);
return std::string() + static_cast<char>(toupper(c1)) +
static_cast<char>(toupper(c2));
}
void ShareTargets::NotifyObserver(ShareTargetsObserver* observer) {
std::string locale =
GetCountryStringFromID(country_codes::GetCurrentCountryID());
auto it = targets_->map_target_locale_map().find(locale);
if (it == targets_->map_target_locale_map().end()) {
it = targets_->map_target_locale_map().find(GLOBAL);
}
std::unique_ptr<mojom::ShareTargets> to_return(new mojom::ShareTargets());
to_return->CopyFrom(it->second);
observer->OnShareTargetsUpdated(std::move(to_return));
}
void ShareTargets::NotifyShareTargetUpdated() {
if (!targets_)
return;
for (ShareTargetsObserver& observer : observers_) {
NotifyObserver(&observer);
}
}
} // namespace sharing