summaryrefslogtreecommitdiff
path: root/chromium/content/browser/conversions/conversion_policy.cc
blob: d323ccfe3b7c45eda47956dfa01befa5054ec73a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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 "content/browser/conversions/conversion_policy.h"

#include "base/format_macros.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"

namespace content {

namespace {

// Maximum number of allowed conversion metadata values. Higher entropy
// conversion metadata is stripped to these lower bits.
const int kMaxAllowedConversionValues = 8;

}  // namespace

uint64_t ConversionPolicy::NoiseProvider::GetNoisedConversionData(
    uint64_t conversion_data) const {
  // Return |conversion_data| without any noise 95% of the time.
  if (base::RandDouble() > .05)
    return conversion_data;

  // 5% of the time return a random number in the allowed range. Note that the
  // value is noised 5% of the time, but only wrong 5 *
  // (kMaxAllowedConversionValues - 1) / kMaxAllowedConversionValues percent of
  // the time.
  return static_cast<uint64_t>(base::RandInt(0, kMaxAllowedConversionValues));
}

// static
std::unique_ptr<ConversionPolicy> ConversionPolicy::CreateForTesting(
    std::unique_ptr<NoiseProvider> noise_provider) {
  return base::WrapUnique<ConversionPolicy>(
      new ConversionPolicy(std::move(noise_provider)));
}

ConversionPolicy::ConversionPolicy()
    : noise_provider_(std::make_unique<NoiseProvider>()) {}

ConversionPolicy::ConversionPolicy(
    std::unique_ptr<ConversionPolicy::NoiseProvider> noise_provider)
    : noise_provider_(std::move(noise_provider)) {}

ConversionPolicy::~ConversionPolicy() = default;

base::Time ConversionPolicy::GetReportTimeForConversion(
    const ConversionReport& report) const {
  // After the initial impression, a schedule of reporting windows and deadlines
  // associated with that impression begins. The time between impression time
  // and impression expiry is split into multiple reporting windows. At the end
  // of each window, the browser will send all scheduled reports for that
  // impression.
  //
  // Each reporting window has a deadline and only conversions registered before
  // that deadline are sent in that window. Each deadline is one hour prior to
  // the window report time. The deadlines relative to impression time are <2
  // days minus 1 hour, 7 days minus 1 hour, impression expiry>. The impression
  // expiry window is only used for conversions that occur after the 7 day
  // deadline. For example, a conversion which happens one hour after an
  // impression with an expiry of two hours, is still reported in the 2 day
  // window.
  constexpr base::TimeDelta kWindowDeadlineOffset =
      base::TimeDelta::FromHours(1);
  base::TimeDelta expiry_deadline =
      report.impression.expiry_time() - report.impression.impression_time();
  const base::TimeDelta kReportingWindowDeadlines[] = {
      base::TimeDelta::FromDays(2) - kWindowDeadlineOffset,
      base::TimeDelta::FromDays(7) - kWindowDeadlineOffset, expiry_deadline};

  base::TimeDelta deadline_to_use;

  // Given a conversion report that was created at |report.report_time|, find
  // the first applicable reporting window this conversion should be reported
  // at.
  for (base::TimeDelta report_window_deadline : kReportingWindowDeadlines) {
    // If this window is valid for the conversion, use it. |report.report_time|
    // is roughly ~now, as the conversion time is used as the default value for
    // newly created reports that have not had a report time set.
    if (report.impression.impression_time() + report_window_deadline >=
        report.report_time) {
      deadline_to_use = report_window_deadline;
      break;
    }
  }

  // Valid conversion reports should always have a valid reporting deadline.
  DCHECK(!deadline_to_use.is_zero());

  // If the expiry deadline falls after the first window, but before another
  // window, use it instead. For example, if expiry is at 3 days, we can send
  // reports at the 2 day deadline and the expiry deadline instead of at the 7
  // day deadline.
  if (expiry_deadline > kReportingWindowDeadlines[0] &&
      expiry_deadline < deadline_to_use) {
    deadline_to_use = expiry_deadline;
  }

  return report.impression.impression_time() + deadline_to_use +
         kWindowDeadlineOffset;
}

int ConversionPolicy::GetMaxConversionsPerImpression() const {
  return 3;
}

void ConversionPolicy::AssignAttributionCredits(
    std::vector<ConversionReport>* reports) const {
  DCHECK(!reports->empty());
  ConversionReport* last_report = &(reports->at(0));

  // Give the latest impression an attribution of 100 and all the rest 0.
  for (auto& report : *reports) {
    report.attribution_credit = 0;
    if (report.impression.impression_time() >
        last_report->impression.impression_time())
      last_report = &report;
  }

  last_report->attribution_credit = 100;
}

std::string ConversionPolicy::GetSanitizedConversionData(
    uint64_t conversion_data) const {
  // Add noise to the conversion when the value is first sanitized from a
  // conversion registration event. This noised data will be used for all
  // associated impressions that convert.
  conversion_data = noise_provider_->GetNoisedConversionData(conversion_data);

  // Allow at most 3 bits of entropy in conversion data. base::StringPrintf() is
  // used over base::HexEncode() because HexEncode() returns a hex string with
  // little-endian ordering. Big-endian ordering is expected here because the
  // API assumes big-endian when parsing attributes.
  return base::StringPrintf("%" PRIx64,
                            conversion_data % kMaxAllowedConversionValues);
}

}  // namespace content