blob: 297511318688072b502d72d7d8cf9be3bcb9f371 [file] [log] [blame]
// Copyright 2015 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/android/data_usage/data_use_matcher.h"
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
namespace chrome {
namespace android {
DataUseMatcher::DataUseMatcher(
const base::Callback<void(const std::string&)>&
on_tracking_label_removed_callback,
const base::Callback<void(bool)>& on_matching_rules_fetched_callback,
const base::TimeDelta& default_matching_rule_expiration_duration)
: default_matching_rule_expiration_duration_(
default_matching_rule_expiration_duration),
tick_clock_(new base::DefaultTickClock()),
on_tracking_label_removed_callback_(on_tracking_label_removed_callback),
on_matching_rules_fetched_callback_(on_matching_rules_fetched_callback) {
DCHECK(on_tracking_label_removed_callback_);
DCHECK(on_matching_rules_fetched_callback_);
// Detach from current thread since rest of DataUseMatcher lives on the UI
// thread and the current thread may not be UI thread..
thread_checker_.DetachFromThread();
}
DataUseMatcher::~DataUseMatcher() {}
void DataUseMatcher::RegisterURLRegexes(
const std::vector<std::string>& app_package_names,
const std::vector<std::string>& domain_path_regexes,
const std::vector<std::string>& labels) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(app_package_names.size(), domain_path_regexes.size());
DCHECK_EQ(app_package_names.size(), labels.size());
base::hash_set<std::string> removed_matching_rule_labels;
uint32_t invalid_rules = 0;
for (const auto& matching_rule : matching_rules_)
removed_matching_rule_labels.insert(matching_rule->label());
matching_rules_.clear();
re2::RE2::Options options(re2::RE2::DefaultOptions);
options.set_case_sensitive(false);
for (size_t i = 0; i < domain_path_regexes.size(); ++i) {
const std::string& url_regex = domain_path_regexes.at(i);
std::string app_package_name;
base::TimeTicks expiration;
const base::TimeTicks now_ticks = tick_clock_->NowTicks();
ParsePackageField(app_package_names.at(i), &app_package_name, &expiration);
if (url_regex.empty() && app_package_name.empty()) {
invalid_rules++;
continue;
}
std::unique_ptr<re2::RE2> pattern(new re2::RE2(url_regex, options));
if (!pattern->ok()) {
invalid_rules++;
continue;
}
if (expiration <= now_ticks)
continue; // skip expired matching rules.
DCHECK(!labels.at(i).empty());
matching_rules_.push_back(base::MakeUnique<MatchingRule>(
app_package_name, std::move(pattern), labels.at(i), expiration));
removed_matching_rule_labels.erase(labels.at(i));
}
for (const std::string& label : removed_matching_rule_labels)
on_tracking_label_removed_callback_.Run(label);
UMA_HISTOGRAM_COUNTS_100("DataUsage.MatchingRulesCount.Valid",
matching_rules_.size());
UMA_HISTOGRAM_COUNTS_100("DataUsage.MatchingRulesCount.Invalid",
invalid_rules);
on_matching_rules_fetched_callback_.Run(!matching_rules_.empty());
}
bool DataUseMatcher::MatchesURL(const GURL& url, std::string* label) const {
const base::TimeTicks now_ticks = tick_clock_->NowTicks();
DCHECK(thread_checker_.CalledOnValidThread());
*label = "";
if (!url.is_valid() || url.is_empty())
return false;
for (const auto& matching_rule : matching_rules_) {
if (matching_rule->expiration() <= now_ticks)
continue; // skip expired matching rules.
base::TimeTicks begin = base::TimeTicks::Now();
bool match = re2::RE2::FullMatch(url.spec(), *(matching_rule->pattern()));
UMA_HISTOGRAM_TIMES("DataUsage.Perf.URLRegexMatchDuration",
base::TimeTicks::Now() - begin);
if (match) {
*label = matching_rule->label();
return true;
}
}
return false;
}
bool DataUseMatcher::MatchesAppPackageName(const std::string& app_package_name,
std::string* label) const {
const base::TimeTicks now_ticks = tick_clock_->NowTicks();
DCHECK(thread_checker_.CalledOnValidThread());
*label = "";
if (app_package_name.empty())
return false;
for (const auto& matching_rule : matching_rules_) {
if (matching_rule->expiration() <= now_ticks)
continue; // skip expired matching rules.
if (app_package_name == matching_rule->app_package_name()) {
*label = matching_rule->label();
return true;
}
}
return false;
}
bool DataUseMatcher::HasRules() const {
return !matching_rules_.empty();
}
bool DataUseMatcher::HasValidRuleWithLabel(const std::string& label) const {
for (const auto& matching_rule : matching_rules_) {
if (matching_rule->expiration() > tick_clock_->NowTicks() &&
label == matching_rule->label()) {
return true;
}
}
return false;
}
void DataUseMatcher::ParsePackageField(const std::string& app_package_name,
std::string* new_app_package_name,
base::TimeTicks* expiration) const {
const char separator = '|';
size_t index = app_package_name.find_last_of(separator);
uint64_t
expiration_milliseconds; // expiration time as milliSeconds since epoch.
if (index != std::string::npos &&
base::StringToUint64(app_package_name.substr(index + 1),
&expiration_milliseconds)) {
*new_app_package_name = app_package_name.substr(0, index);
*expiration = base::TimeTicks::UnixEpoch() +
base::TimeDelta::FromMilliseconds(expiration_milliseconds);
return;
}
*expiration =
tick_clock_->NowTicks() + default_matching_rule_expiration_duration_;
*new_app_package_name = app_package_name;
}
DataUseMatcher::MatchingRule::MatchingRule(const std::string& app_package_name,
std::unique_ptr<re2::RE2> pattern,
const std::string& label,
const base::TimeTicks& expiration)
: app_package_name_(app_package_name),
pattern_(std::move(pattern)),
label_(label),
expiration_(expiration) {}
DataUseMatcher::MatchingRule::~MatchingRule() {}
const re2::RE2* DataUseMatcher::MatchingRule::pattern() const {
return pattern_.get();
}
const std::string& DataUseMatcher::MatchingRule::app_package_name() const {
return app_package_name_;
}
const std::string& DataUseMatcher::MatchingRule::label() const {
return label_;
}
const base::TimeTicks& DataUseMatcher::MatchingRule::expiration() const {
return expiration_;
}
} // namespace android
} // namespace chrome