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/local_device_data/nearby_share_local_device_data_manager_impl.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/notreached.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_util.h"
14 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
15 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater.h"
16 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_device_data_updater_impl.h"
17 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler.h"
18 #include "chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_factory.h"
19 #include "components/prefs/pref_service.h"
20
21 namespace {
22
23 // Using the alphanumeric characters below, this provides 36^10 unique device
24 // IDs. Note that the uniqueness requirement is not global; the IDs are only
25 // used to differentiate between devices associated with a single GAIA account.
26 // This ID length agrees with the GmsCore implementation.
27 const size_t kDeviceIdLength = 10;
28
29 // Possible characters used in a randomly generated device ID. This agrees with
30 // the GmsCore implementation.
31 constexpr std::array<char, 36> kAlphaNumericChars = {
32 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
33 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
34 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
35
36 constexpr base::TimeDelta kUpdateDeviceDataTimeout =
37 base::TimeDelta::FromSeconds(30);
38 constexpr base::TimeDelta kDeviceDataDownloadPeriod =
39 base::TimeDelta::FromHours(12);
40
41 } // namespace
42
43 // static
44 NearbyShareLocalDeviceDataManagerImpl::Factory*
45 NearbyShareLocalDeviceDataManagerImpl::Factory::test_factory_ = nullptr;
46
47 // static
48 std::unique_ptr<NearbyShareLocalDeviceDataManager>
Create(PrefService * pref_service,NearbyShareClientFactory * http_client_factory,const std::string & default_device_name)49 NearbyShareLocalDeviceDataManagerImpl::Factory::Create(
50 PrefService* pref_service,
51 NearbyShareClientFactory* http_client_factory,
52 const std::string& default_device_name) {
53 if (test_factory_) {
54 return test_factory_->CreateInstance(pref_service, http_client_factory,
55 default_device_name);
56 }
57
58 return base::WrapUnique(new NearbyShareLocalDeviceDataManagerImpl(
59 pref_service, http_client_factory, default_device_name));
60 }
61
62 // static
SetFactoryForTesting(Factory * test_factory)63 void NearbyShareLocalDeviceDataManagerImpl::Factory::SetFactoryForTesting(
64 Factory* test_factory) {
65 test_factory_ = test_factory;
66 }
67
68 NearbyShareLocalDeviceDataManagerImpl::Factory::~Factory() = default;
69
NearbyShareLocalDeviceDataManagerImpl(PrefService * pref_service,NearbyShareClientFactory * http_client_factory,const std::string & default_device_name)70 NearbyShareLocalDeviceDataManagerImpl::NearbyShareLocalDeviceDataManagerImpl(
71 PrefService* pref_service,
72 NearbyShareClientFactory* http_client_factory,
73 const std::string& default_device_name)
74 : pref_service_(pref_service),
75 device_data_updater_(NearbyShareDeviceDataUpdaterImpl::Factory::Create(
76 GetId(),
77 kUpdateDeviceDataTimeout,
78 http_client_factory)),
79 download_device_data_scheduler_(
80 NearbyShareSchedulerFactory::CreatePeriodicScheduler(
81 kDeviceDataDownloadPeriod,
82 /*retry_failures=*/true,
83 /*require_connectivity=*/true,
84 prefs::kNearbySharingSchedulerDownloadDeviceDataPrefName,
85 pref_service_,
86 base::BindRepeating(&NearbyShareLocalDeviceDataManagerImpl::
87 OnDownloadDeviceDataRequested,
88 base::Unretained(this)))) {
89 DCHECK(!default_device_name.empty());
90 if (GetDeviceName().empty())
91 SetDeviceName(default_device_name);
92 }
93
94 NearbyShareLocalDeviceDataManagerImpl::
95 ~NearbyShareLocalDeviceDataManagerImpl() = default;
96
GetId()97 std::string NearbyShareLocalDeviceDataManagerImpl::GetId() {
98 std::string id =
99 pref_service_->GetString(prefs::kNearbySharingDeviceIdPrefName);
100 if (!id.empty())
101 return id;
102
103 for (size_t i = 0; i < kDeviceIdLength; ++i)
104 id += kAlphaNumericChars[base::RandGenerator(kAlphaNumericChars.size())];
105
106 pref_service_->SetString(prefs::kNearbySharingDeviceIdPrefName, id);
107
108 return id;
109 }
110
GetDeviceName() const111 std::string NearbyShareLocalDeviceDataManagerImpl::GetDeviceName() const {
112 return pref_service_->GetString(prefs::kNearbySharingDeviceNamePrefName);
113 }
114
GetFullName() const115 base::Optional<std::string> NearbyShareLocalDeviceDataManagerImpl::GetFullName()
116 const {
117 std::string name =
118 pref_service_->GetString(prefs::kNearbySharingFullNamePrefName);
119 if (name.empty())
120 return base::nullopt;
121
122 return name;
123 }
124
GetIconUrl() const125 base::Optional<std::string> NearbyShareLocalDeviceDataManagerImpl::GetIconUrl()
126 const {
127 std::string url =
128 pref_service_->GetString(prefs::kNearbySharingIconUrlPrefName);
129 if (url.empty())
130 return base::nullopt;
131
132 return url;
133 }
134
135 nearby_share::mojom::DeviceNameValidationResult
ValidateDeviceName(const std::string & name)136 NearbyShareLocalDeviceDataManagerImpl::ValidateDeviceName(
137 const std::string& name) {
138 if (name.empty())
139 return nearby_share::mojom::DeviceNameValidationResult::kErrorEmpty;
140
141 if (!base::IsStringUTF8(name))
142 return nearby_share::mojom::DeviceNameValidationResult::kErrorNotValidUtf8;
143
144 if (name.length() > kNearbyShareDeviceNameMaxLength)
145 return nearby_share::mojom::DeviceNameValidationResult::kErrorTooLong;
146
147 return nearby_share::mojom::DeviceNameValidationResult::kValid;
148 }
149
150 nearby_share::mojom::DeviceNameValidationResult
SetDeviceName(const std::string & name)151 NearbyShareLocalDeviceDataManagerImpl::SetDeviceName(const std::string& name) {
152 if (name == GetDeviceName())
153 return nearby_share::mojom::DeviceNameValidationResult::kValid;
154
155 auto error = ValidateDeviceName(name);
156 if (error != nearby_share::mojom::DeviceNameValidationResult::kValid)
157 return error;
158
159 pref_service_->SetString(prefs::kNearbySharingDeviceNamePrefName, name);
160
161 NotifyLocalDeviceDataChanged(/*did_device_name_change=*/true,
162 /*did_full_name_change=*/false,
163 /*did_icon_url_change=*/false);
164
165 return nearby_share::mojom::DeviceNameValidationResult::kValid;
166 }
167
DownloadDeviceData()168 void NearbyShareLocalDeviceDataManagerImpl::DownloadDeviceData() {
169 download_device_data_scheduler_->MakeImmediateRequest();
170 }
171
UploadContacts(std::vector<nearbyshare::proto::Contact> contacts,UploadCompleteCallback callback)172 void NearbyShareLocalDeviceDataManagerImpl::UploadContacts(
173 std::vector<nearbyshare::proto::Contact> contacts,
174 UploadCompleteCallback callback) {
175 device_data_updater_->UpdateDeviceData(
176 std::move(contacts),
177 /*certificates=*/base::nullopt,
178 base::BindOnce(
179 &NearbyShareLocalDeviceDataManagerImpl::OnUploadContactsFinished,
180 base::Unretained(this), std::move(callback)));
181 }
182
UploadCertificates(std::vector<nearbyshare::proto::PublicCertificate> certificates,UploadCompleteCallback callback)183 void NearbyShareLocalDeviceDataManagerImpl::UploadCertificates(
184 std::vector<nearbyshare::proto::PublicCertificate> certificates,
185 UploadCompleteCallback callback) {
186 device_data_updater_->UpdateDeviceData(
187 /*contacts=*/base::nullopt, std::move(certificates),
188 base::BindOnce(
189 &NearbyShareLocalDeviceDataManagerImpl::OnUploadCertificatesFinished,
190 base::Unretained(this), std::move(callback)));
191 }
192
OnStart()193 void NearbyShareLocalDeviceDataManagerImpl::OnStart() {
194 // This schedules an immediate download of the full name and icon URL from the
195 // server if that has never happened before.
196 download_device_data_scheduler_->Start();
197 }
198
OnStop()199 void NearbyShareLocalDeviceDataManagerImpl::OnStop() {
200 download_device_data_scheduler_->Stop();
201 }
202
OnDownloadDeviceDataRequested()203 void NearbyShareLocalDeviceDataManagerImpl::OnDownloadDeviceDataRequested() {
204 device_data_updater_->UpdateDeviceData(
205 /*contacts=*/base::nullopt,
206 /*certificates=*/base::nullopt,
207 base::BindOnce(
208 &NearbyShareLocalDeviceDataManagerImpl::OnDownloadDeviceDataFinished,
209 base::Unretained(this)));
210 }
211
OnDownloadDeviceDataFinished(const base::Optional<nearbyshare::proto::UpdateDeviceResponse> & response)212 void NearbyShareLocalDeviceDataManagerImpl::OnDownloadDeviceDataFinished(
213 const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
214 if (response)
215 HandleUpdateDeviceResponse(response);
216
217 download_device_data_scheduler_->HandleResult(
218 /*success=*/response.has_value());
219 }
220
OnUploadContactsFinished(UploadCompleteCallback callback,const base::Optional<nearbyshare::proto::UpdateDeviceResponse> & response)221 void NearbyShareLocalDeviceDataManagerImpl::OnUploadContactsFinished(
222 UploadCompleteCallback callback,
223 const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
224 if (response)
225 HandleUpdateDeviceResponse(response);
226
227 std::move(callback).Run(/*success=*/response.has_value());
228 }
229
OnUploadCertificatesFinished(UploadCompleteCallback callback,const base::Optional<nearbyshare::proto::UpdateDeviceResponse> & response)230 void NearbyShareLocalDeviceDataManagerImpl::OnUploadCertificatesFinished(
231 UploadCompleteCallback callback,
232 const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
233 if (response)
234 HandleUpdateDeviceResponse(response);
235
236 std::move(callback).Run(/*success=*/response.has_value());
237 }
238
HandleUpdateDeviceResponse(const base::Optional<nearbyshare::proto::UpdateDeviceResponse> & response)239 void NearbyShareLocalDeviceDataManagerImpl::HandleUpdateDeviceResponse(
240 const base::Optional<nearbyshare::proto::UpdateDeviceResponse>& response) {
241 if (!response)
242 return;
243
244 bool did_full_name_change = response->person_name() != GetFullName();
245 bool did_icon_url_change = response->image_url() != GetIconUrl();
246 if (!did_full_name_change && !did_icon_url_change)
247 return;
248
249 if (did_full_name_change) {
250 pref_service_->SetString(prefs::kNearbySharingFullNamePrefName,
251 response->person_name());
252 }
253 if (did_icon_url_change) {
254 pref_service_->SetString(prefs::kNearbySharingIconUrlPrefName,
255 response->image_url());
256 }
257
258 NotifyLocalDeviceDataChanged(/*did_device_name_change=*/false,
259 did_full_name_change, did_icon_url_change);
260 }
261