1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_downloader_impl.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/memory/ptr_util.h"
11 #include "chrome/browser/nearby_sharing/client/nearby_share_client.h"
12 #include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
13 #include "chrome/browser/nearby_sharing/logging/logging.h"
14
15 namespace {
16
RecordListContactPeopleResultMetrics(NearbyShareHttpResult result,size_t current_page_number)17 void RecordListContactPeopleResultMetrics(NearbyShareHttpResult result,
18 size_t current_page_number) {
19 // TODO(https://crbug.com/1105579): Record a histogram value for each result.
20 // TODO(https://crbug.com/1105579): On failure, record a histogram value for
21 // the page that the request failed on.
22 }
23
RecordContactDownloadMetrics(const std::vector<nearbyshare::proto::ContactRecord> & contacts,size_t num_pages)24 void RecordContactDownloadMetrics(
25 const std::vector<nearbyshare::proto::ContactRecord>& contacts,
26 size_t num_pages) {
27 // TODO(https://crbug.com/1105579): Record a histogram for the total number of
28 // pages needed, the ratio of contact types, and the ratio of (un)reachable
29 // contacts.
30 }
31
32 } // namespace
33
34 // static
35 NearbyShareContactDownloaderImpl::Factory*
36 NearbyShareContactDownloaderImpl::Factory::test_factory_ = nullptr;
37
38 // static
39 std::unique_ptr<NearbyShareContactDownloader>
Create(const std::string & device_id,base::TimeDelta timeout,NearbyShareClientFactory * client_factory,SuccessCallback success_callback,FailureCallback failure_callback)40 NearbyShareContactDownloaderImpl::Factory::Create(
41 const std::string& device_id,
42 base::TimeDelta timeout,
43 NearbyShareClientFactory* client_factory,
44 SuccessCallback success_callback,
45 FailureCallback failure_callback) {
46 if (test_factory_)
47 return test_factory_->CreateInstance(device_id, timeout, client_factory,
48 std::move(success_callback),
49 std::move(failure_callback));
50
51 return base::WrapUnique(new NearbyShareContactDownloaderImpl(
52 device_id, timeout, client_factory, std::move(success_callback),
53 std::move(failure_callback)));
54 }
55
56 // static
SetFactoryForTesting(Factory * test_factory)57 void NearbyShareContactDownloaderImpl::Factory::SetFactoryForTesting(
58 Factory* test_factory) {
59 test_factory_ = test_factory;
60 }
61
62 NearbyShareContactDownloaderImpl::Factory::~Factory() = default;
63
NearbyShareContactDownloaderImpl(const std::string & device_id,base::TimeDelta timeout,NearbyShareClientFactory * client_factory,SuccessCallback success_callback,FailureCallback failure_callback)64 NearbyShareContactDownloaderImpl::NearbyShareContactDownloaderImpl(
65 const std::string& device_id,
66 base::TimeDelta timeout,
67 NearbyShareClientFactory* client_factory,
68 SuccessCallback success_callback,
69 FailureCallback failure_callback)
70 : NearbyShareContactDownloader(device_id,
71 std::move(success_callback),
72 std::move(failure_callback)),
73 timeout_(timeout),
74 client_factory_(client_factory) {}
75
76 NearbyShareContactDownloaderImpl::~NearbyShareContactDownloaderImpl() = default;
77
OnRun()78 void NearbyShareContactDownloaderImpl::OnRun() {
79 NS_LOG(VERBOSE) << __func__ << ": Starting contacts download.";
80 CallListContactPeople(/*next_page_token=*/base::nullopt);
81 }
82
CallListContactPeople(const base::Optional<std::string> & next_page_token)83 void NearbyShareContactDownloaderImpl::CallListContactPeople(
84 const base::Optional<std::string>& next_page_token) {
85 ++current_page_number_;
86 NS_LOG(VERBOSE) << __func__
87 << ": Making ListContactPeople RPC call to fetch page number "
88 << current_page_number_
89 << " with page token: " << next_page_token.value_or("[null]");
90 timer_.Start(
91 FROM_HERE, timeout_,
92 base::BindOnce(
93 &NearbyShareContactDownloaderImpl::OnListContactPeopleTimeout,
94 base::Unretained(this)));
95
96 nearbyshare::proto::ListContactPeopleRequest request;
97 if (next_page_token)
98 request.set_page_token(*next_page_token);
99
100 client_ = client_factory_->CreateInstance();
101 client_->ListContactPeople(
102 request,
103 base::BindOnce(
104 &NearbyShareContactDownloaderImpl::OnListContactPeopleSuccess,
105 base::Unretained(this)),
106 base::BindOnce(
107 &NearbyShareContactDownloaderImpl::OnListContactPeopleFailure,
108 base::Unretained(this)));
109 }
110
OnListContactPeopleSuccess(const nearbyshare::proto::ListContactPeopleResponse & response)111 void NearbyShareContactDownloaderImpl::OnListContactPeopleSuccess(
112 const nearbyshare::proto::ListContactPeopleResponse& response) {
113 timer_.Stop();
114 contacts_.insert(contacts_.end(), response.contact_records().begin(),
115 response.contact_records().end());
116 base::Optional<std::string> next_page_token =
117 response.next_page_token().empty()
118 ? base::nullopt
119 : base::make_optional<std::string>(response.next_page_token());
120 client_.reset();
121 RecordListContactPeopleResultMetrics(NearbyShareHttpResult::kSuccess,
122 current_page_number_);
123
124 if (next_page_token) {
125 CallListContactPeople(next_page_token);
126 return;
127 }
128
129 NS_LOG(VERBOSE) << __func__ << ": Download of " << contacts_.size()
130 << " contacts succeeded.";
131 RecordContactDownloadMetrics(contacts_, current_page_number_);
132
133 // Remove device contacts if the feature flag is disabled.
134 if (!base::FeatureList::IsEnabled(features::kNearbySharingDeviceContacts)) {
135 size_t initial_num_contacts = contacts_.size();
136 contacts_.erase(
137 std::remove_if(
138 contacts_.begin(), contacts_.end(),
139 [](const nearbyshare::proto::ContactRecord& contact) {
140 return contact.type() ==
141 nearbyshare::proto::ContactRecord::DEVICE_CONTACT;
142 }),
143 contacts_.end());
144 NS_LOG(VERBOSE) << __func__ << ": Removed "
145 << initial_num_contacts - contacts_.size()
146 << " device contacts.";
147 }
148
149 // Remove unreachable contacts.
150 size_t initial_num_contacts = contacts_.size();
151 contacts_.erase(
152 std::remove_if(contacts_.begin(), contacts_.end(),
153 [](const nearbyshare::proto::ContactRecord& contact) {
154 return !contact.is_reachable();
155 }),
156 contacts_.end());
157 uint32_t num_unreachable_contacts_filtered_out =
158 initial_num_contacts - contacts_.size();
159 NS_LOG(VERBOSE) << __func__ << ": Removed "
160 << num_unreachable_contacts_filtered_out
161 << " unreachable contacts.";
162
163 Succeed(std::move(contacts_), num_unreachable_contacts_filtered_out);
164 }
165
OnListContactPeopleFailure(NearbyShareHttpError error)166 void NearbyShareContactDownloaderImpl::OnListContactPeopleFailure(
167 NearbyShareHttpError error) {
168 timer_.Stop();
169 client_.reset();
170 RecordListContactPeopleResultMetrics(NearbyShareHttpErrorToResult(error),
171 current_page_number_);
172
173 NS_LOG(ERROR) << __func__ << ": Contact download RPC call failed with error "
174 << error << " fetching page number " << current_page_number_;
175 Fail();
176 }
177
OnListContactPeopleTimeout()178 void NearbyShareContactDownloaderImpl::OnListContactPeopleTimeout() {
179 client_.reset();
180 RecordListContactPeopleResultMetrics(NearbyShareHttpResult::kTimeout,
181 current_page_number_);
182
183 NS_LOG(ERROR) << __func__ << ": Contact download RPC call timed out.";
184 Fail();
185 }
186