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