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