fast_pair_repository_impl.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // Copyright 2021 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. #include "ash/quick_pair/repository/fast_pair_repository_impl.h"
  5. #include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
  6. #include "ash/quick_pair/common/logging.h"
  7. #include "ash/quick_pair/proto/fastpair.pb.h"
  8. #include "ash/quick_pair/proto/fastpair_data.pb.h"
  9. #include "ash/quick_pair/repository/fast_pair/device_id_map.h"
  10. #include "ash/quick_pair/repository/fast_pair/device_image_store.h"
  11. #include "ash/quick_pair/repository/fast_pair/device_metadata_fetcher.h"
  12. #include "ash/quick_pair/repository/fast_pair/fast_pair_image_decoder_impl.h"
  13. #include "ash/quick_pair/repository/fast_pair/footprints_fetcher.h"
  14. #include "ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.h"
  15. #include "ash/quick_pair/repository/fast_pair/proto_conversions.h"
  16. #include "ash/quick_pair/repository/fast_pair/saved_device_registry.h"
  17. #include "ash/services/quick_pair/public/cpp/account_key_filter.h"
  18. #include "base/callback_helpers.h"
  19. #include "base/memory/scoped_refptr.h"
  20. #include "base/strings/string_number_conversions.h"
  21. #include "base/strings/stringprintf.h"
  22. #include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
  23. #include "device/bluetooth/bluetooth_device.h"
  24. namespace ash {
  25. namespace quick_pair {
  26. FastPairRepositoryImpl::FastPairRepositoryImpl()
  27. : FastPairRepository(),
  28. device_metadata_fetcher_(std::make_unique<DeviceMetadataFetcher>()),
  29. footprints_fetcher_(std::make_unique<FootprintsFetcherImpl>()),
  30. image_decoder_(std::make_unique<FastPairImageDecoderImpl>()),
  31. device_id_map_(std::make_unique<DeviceIdMap>()),
  32. device_image_store_(
  33. std::make_unique<DeviceImageStore>(image_decoder_.get())),
  34. saved_device_registry_(std::make_unique<SavedDeviceRegistry>()),
  35. footprints_last_updated_(base::Time::UnixEpoch()) {}
  36. FastPairRepositoryImpl::FastPairRepositoryImpl(
  37. std::unique_ptr<DeviceMetadataFetcher> device_metadata_fetcher,
  38. std::unique_ptr<FootprintsFetcher> footprints_fetcher,
  39. std::unique_ptr<FastPairImageDecoder> image_decoder,
  40. std::unique_ptr<DeviceIdMap> device_id_map,
  41. std::unique_ptr<DeviceImageStore> device_image_store,
  42. std::unique_ptr<SavedDeviceRegistry> saved_device_registry)
  43. : FastPairRepository(),
  44. device_metadata_fetcher_(std::move(device_metadata_fetcher)),
  45. footprints_fetcher_(std::move(footprints_fetcher)),
  46. image_decoder_(std::move(image_decoder)),
  47. device_id_map_(std::move(device_id_map)),
  48. device_image_store_(std::move(device_image_store)),
  49. saved_device_registry_(std::move(saved_device_registry)),
  50. footprints_last_updated_(base::Time::UnixEpoch()) {}
  51. FastPairRepositoryImpl::~FastPairRepositoryImpl() = default;
  52. void FastPairRepositoryImpl::GetDeviceMetadata(
  53. const std::string& hex_model_id,
  54. DeviceMetadataCallback callback) {
  55. std::string normalized_id = base::ToUpperASCII(hex_model_id);
  56. if (metadata_cache_.contains(normalized_id)) {
  57. QP_LOG(VERBOSE) << __func__ << ": Data already in cache.";
  58. RecordFastPairRepositoryCacheResult(/*success=*/true);
  59. std::move(callback).Run(metadata_cache_[normalized_id].get(),
  60. /*has_retryable_error=*/false);
  61. return;
  62. }
  63. QP_LOG(VERBOSE) << __func__ << ": Not cached, fetching from web service.";
  64. RecordFastPairRepositoryCacheResult(/*success=*/false);
  65. device_metadata_fetcher_->LookupHexDeviceId(
  66. normalized_id, base::BindOnce(&FastPairRepositoryImpl::OnMetadataFetched,
  67. weak_ptr_factory_.GetWeakPtr(),
  68. normalized_id, std::move(callback)));
  69. }
  70. void FastPairRepositoryImpl::OnMetadataFetched(
  71. const std::string& normalized_model_id,
  72. DeviceMetadataCallback callback,
  73. absl::optional<nearby::fastpair::GetObservedDeviceResponse> response,
  74. bool has_retryable_error) {
  75. if (!response) {
  76. std::move(callback).Run(nullptr, has_retryable_error);
  77. return;
  78. }
  79. if (response->image().empty()) {
  80. metadata_cache_[normalized_model_id] =
  81. std::make_unique<DeviceMetadata>(std::move(*response), gfx::Image());
  82. std::move(callback).Run(metadata_cache_[normalized_model_id].get(),
  83. /*has_retryable_error=*/false);
  84. return;
  85. }
  86. const std::string& string_data = response->image();
  87. std::vector<uint8_t> binary_data(string_data.begin(), string_data.end());
  88. image_decoder_->DecodeImage(
  89. binary_data,
  90. /*resize_to_notification_size=*/true,
  91. base::BindOnce(&FastPairRepositoryImpl::OnImageDecoded,
  92. weak_ptr_factory_.GetWeakPtr(), normalized_model_id,
  93. std::move(callback), *response));
  94. }
  95. void FastPairRepositoryImpl::OnImageDecoded(
  96. const std::string& normalized_model_id,
  97. DeviceMetadataCallback callback,
  98. nearby::fastpair::GetObservedDeviceResponse response,
  99. gfx::Image image) {
  100. metadata_cache_[normalized_model_id] =
  101. std::make_unique<DeviceMetadata>(response, std::move(image));
  102. std::move(callback).Run(metadata_cache_[normalized_model_id].get(),
  103. /*has_retryable_error=*/false);
  104. }
  105. bool FastPairRepositoryImpl::IsAccountKeyPairedLocally(
  106. const std::vector<uint8_t>& account_key) {
  107. return saved_device_registry_->IsAccountKeySavedToRegistry(account_key);
  108. }
  109. void FastPairRepositoryImpl::CheckAccountKeys(
  110. const AccountKeyFilter& account_key_filter,
  111. CheckAccountKeysCallback callback) {
  112. CheckAccountKeysImpl(account_key_filter, std::move(callback),
  113. /*refresh_cache_on_miss=*/true);
  114. }
  115. void FastPairRepositoryImpl::CheckAccountKeysImpl(
  116. const AccountKeyFilter& account_key_filter,
  117. CheckAccountKeysCallback callback,
  118. bool refresh_cache_on_miss) {
  119. QP_LOG(INFO) << __func__;
  120. for (const auto& info : user_devices_cache_.fast_pair_info()) {
  121. if (info.has_device()) {
  122. const std::string& string_key = info.device().account_key();
  123. const std::vector<uint8_t> binary_key(string_key.begin(),
  124. string_key.end());
  125. if (account_key_filter.Test(binary_key)) {
  126. nearby::fastpair::StoredDiscoveryItem device;
  127. if (device.ParseFromString(info.device().discovery_item_bytes())) {
  128. QP_LOG(INFO) << "Account key matched with a paired device: "
  129. << device.title();
  130. GetDeviceMetadata(
  131. device.id(),
  132. base::BindOnce(&FastPairRepositoryImpl::CompleteAccountKeyLookup,
  133. weak_ptr_factory_.GetWeakPtr(),
  134. std::move(callback), std::move(binary_key)));
  135. return;
  136. }
  137. }
  138. }
  139. }
  140. if (refresh_cache_on_miss &&
  141. (base::Time::Now() - footprints_last_updated_) > base::Minutes(1)) {
  142. footprints_fetcher_->GetUserDevices(
  143. base::BindOnce(&FastPairRepositoryImpl::RetryCheckAccountKeys,
  144. weak_ptr_factory_.GetWeakPtr(), account_key_filter,
  145. std::move(callback)));
  146. return;
  147. }
  148. std::move(callback).Run(absl::nullopt);
  149. }
  150. void FastPairRepositoryImpl::RetryCheckAccountKeys(
  151. const AccountKeyFilter& account_key_filter,
  152. CheckAccountKeysCallback callback,
  153. absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
  154. QP_LOG(INFO) << __func__;
  155. if (!user_devices) {
  156. std::move(callback).Run(absl::nullopt);
  157. return;
  158. }
  159. UpdateUserDevicesCache(user_devices);
  160. CheckAccountKeysImpl(account_key_filter, std::move(callback),
  161. /*refresh_cache_on_miss=*/false);
  162. }
  163. void FastPairRepositoryImpl::CompleteAccountKeyLookup(
  164. CheckAccountKeysCallback callback,
  165. const std::vector<uint8_t> account_key,
  166. DeviceMetadata* device_metadata,
  167. bool has_retryable_error) {
  168. if (!device_metadata) {
  169. std::move(callback).Run(absl::nullopt);
  170. return;
  171. }
  172. std::move(callback).Run(
  173. PairingMetadata(device_metadata, std::move(account_key)));
  174. }
  175. void FastPairRepositoryImpl::UpdateUserDevicesCache(
  176. absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
  177. if (user_devices) {
  178. QP_LOG(VERBOSE) << "Updated user devices cache with "
  179. << user_devices->fast_pair_info_size() << " devices.";
  180. user_devices_cache_ = std::move(*user_devices);
  181. footprints_last_updated_ = base::Time::Now();
  182. }
  183. }
  184. void FastPairRepositoryImpl::AssociateAccountKey(
  185. scoped_refptr<Device> device,
  186. const std::vector<uint8_t>& account_key) {
  187. QP_LOG(INFO) << __func__;
  188. DCHECK(device->classic_address());
  189. GetDeviceMetadata(
  190. device->metadata_id,
  191. base::BindOnce(&FastPairRepositoryImpl::AddDeviceToFootprints,
  192. weak_ptr_factory_.GetWeakPtr(), device->metadata_id,
  193. device->classic_address().value(), account_key));
  194. }
  195. bool FastPairRepositoryImpl::AssociateAccountKeyLocally(
  196. scoped_refptr<Device> device) {
  197. QP_LOG(VERBOSE) << __func__;
  198. absl::optional<std::vector<uint8_t>> account_key =
  199. device->GetAdditionalData(Device::AdditionalDataType::kAccountKey);
  200. if (!account_key) {
  201. QP_LOG(WARNING) << __func__ << ": Account key not found for device.";
  202. return false;
  203. }
  204. DCHECK(device->classic_address());
  205. saved_device_registry_->SaveAccountKey(device->classic_address().value(),
  206. account_key.value());
  207. QP_LOG(INFO) << __func__ << ": Saved account key locally.";
  208. return true;
  209. }
  210. void FastPairRepositoryImpl::AddDeviceToFootprints(
  211. const std::string& hex_model_id,
  212. const std::string& mac_address,
  213. const std::vector<uint8_t>& account_key,
  214. DeviceMetadata* metadata,
  215. bool has_retryable_error) {
  216. if (!metadata) {
  217. QP_LOG(WARNING) << __func__ << ": Unable to retrieve metadata.";
  218. return;
  219. }
  220. footprints_fetcher_->AddUserFastPairInfo(
  221. BuildFastPairInfo(hex_model_id, account_key, metadata),
  222. base::BindOnce(&FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete,
  223. weak_ptr_factory_.GetWeakPtr(), mac_address, account_key));
  224. }
  225. void FastPairRepositoryImpl::OnAddDeviceToFootprintsComplete(
  226. const std::string& mac_address,
  227. const std::vector<uint8_t>& account_key,
  228. bool success) {
  229. if (!success) {
  230. // TODO(b/221126805): Handle caching to disk + retries.
  231. return;
  232. }
  233. saved_device_registry_->SaveAccountKey(mac_address, account_key);
  234. }
  235. void FastPairRepositoryImpl::CheckOptInStatus(
  236. CheckOptInStatusCallback callback) {
  237. footprints_fetcher_->GetUserDevices(
  238. base::BindOnce(&FastPairRepositoryImpl::OnCheckOptInStatus,
  239. weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  240. }
  241. void FastPairRepositoryImpl::OnCheckOptInStatus(
  242. CheckOptInStatusCallback callback,
  243. absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
  244. QP_LOG(INFO) << __func__;
  245. if (!user_devices) {
  246. QP_LOG(WARNING)
  247. << __func__
  248. << ": Missing UserReadDevicesResponse from call to Footprints";
  249. std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN);
  250. return;
  251. }
  252. for (const auto& info : user_devices->fast_pair_info()) {
  253. if (info.has_opt_in_status()) {
  254. std::move(callback).Run(info.opt_in_status());
  255. return;
  256. }
  257. }
  258. std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN);
  259. }
  260. void FastPairRepositoryImpl::UpdateOptInStatus(
  261. nearby::fastpair::OptInStatus opt_in_status,
  262. UpdateOptInStatusCallback callback) {
  263. footprints_fetcher_->AddUserFastPairInfo(
  264. BuildFastPairInfoForOptIn(opt_in_status),
  265. base::BindOnce(&FastPairRepositoryImpl::OnUpdateOptInStatusComplete,
  266. weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  267. }
  268. void FastPairRepositoryImpl::OnUpdateOptInStatusComplete(
  269. UpdateOptInStatusCallback callback,
  270. bool success) {
  271. QP_LOG(INFO) << __func__ << ": success=" << success;
  272. std::move(callback).Run(success);
  273. }
  274. void FastPairRepositoryImpl::GetSavedDevices(GetSavedDevicesCallback callback) {
  275. footprints_fetcher_->GetUserDevices(
  276. base::BindOnce(&FastPairRepositoryImpl::OnGetSavedDevices,
  277. weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  278. }
  279. void FastPairRepositoryImpl::OnGetSavedDevices(
  280. GetSavedDevicesCallback callback,
  281. absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
  282. QP_LOG(INFO) << __func__;
  283. RecordGetSavedDevicesResult(/*success=*/user_devices.has_value());
  284. if (!user_devices) {
  285. QP_LOG(WARNING)
  286. << __func__
  287. << ": Missing UserReadDevicesResponse from call to Footprints";
  288. std::move(callback).Run(nearby::fastpair::OptInStatus::STATUS_UNKNOWN, {});
  289. return;
  290. }
  291. nearby::fastpair::OptInStatus opt_in_status =
  292. nearby::fastpair::OptInStatus::STATUS_UNKNOWN;
  293. std::vector<nearby::fastpair::FastPairDevice> saved_devices;
  294. for (const auto& info : user_devices->fast_pair_info()) {
  295. if (info.has_opt_in_status()) {
  296. opt_in_status = info.opt_in_status();
  297. }
  298. if (info.has_device()) {
  299. saved_devices.push_back(info.device());
  300. }
  301. }
  302. // If the opt in status is `STATUS_OPTED_OUT`, then we can expect the list of
  303. // saved devices to be empty, since an opted out status removes all saved
  304. // devices from the list, although there still might be saved devices, if
  305. // an Android or Chromebook writes to the user's account against their wishes.
  306. std::move(callback).Run(opt_in_status, std::move(saved_devices));
  307. }
  308. void FastPairRepositoryImpl::DeleteAssociatedDevice(
  309. const std::string& mac_address,
  310. DeleteAssociatedDeviceCallback callback) {
  311. absl::optional<const std::vector<uint8_t>> account_key =
  312. saved_device_registry_->GetAccountKey(mac_address);
  313. if (!account_key) {
  314. QP_LOG(WARNING) << __func__ << ": No saved account key.";
  315. std::move(callback).Run(/*success=*/false);
  316. return;
  317. }
  318. QP_LOG(VERBOSE) << __func__
  319. << ": Removing device from Footprints with address: "
  320. << mac_address;
  321. footprints_fetcher_->DeleteUserDevice(
  322. base::HexEncode(*account_key),
  323. base::BindOnce(&FastPairRepositoryImpl::OnDeleteAssociatedDevice,
  324. weak_ptr_factory_.GetWeakPtr(), mac_address,
  325. std::move(callback)));
  326. }
  327. void FastPairRepositoryImpl::OnDeleteAssociatedDevice(
  328. const std::string& mac_address,
  329. DeleteAssociatedDeviceCallback callback,
  330. bool success) {
  331. if (!success) {
  332. // TODO(b/221126805): Handle saving pending update to disk + retries.
  333. QP_LOG(WARNING) << __func__ << ": Failed to remove device from Footprints.";
  334. }
  335. QP_LOG(INFO) << __func__ << ": Successfully removed device from Footprints.";
  336. // TODO(b/221126805): Check for successful Footprints delete before deleting
  337. // from the Saved Device registry once Footprints retries are implemented.
  338. if (saved_device_registry_->DeleteAccountKey(mac_address)) {
  339. QP_LOG(INFO) << __func__
  340. << ": Successfully removed device from Saved Device Registry.";
  341. std::move(callback).Run(/*success=*/success);
  342. return;
  343. }
  344. QP_LOG(WARNING) << __func__
  345. << ": Failed to remove device from Saved Device Registry.";
  346. std::move(callback).Run(/*success=*/false);
  347. }
  348. void FastPairRepositoryImpl::DeleteAssociatedDeviceByAccountKey(
  349. const std::vector<uint8_t>& account_key,
  350. DeleteAssociatedDeviceByAccountKeyCallback callback) {
  351. QP_LOG(INFO) << __func__ << ": Removing device from Footprints.";
  352. footprints_fetcher_->DeleteUserDevice(
  353. base::HexEncode(account_key),
  354. base::BindOnce(
  355. &FastPairRepositoryImpl::OnDeleteAssociatedDeviceByAccountKey,
  356. weak_ptr_factory_.GetWeakPtr(), account_key, std::move(callback)));
  357. }
  358. void FastPairRepositoryImpl::OnDeleteAssociatedDeviceByAccountKey(
  359. const std::vector<uint8_t>& account_key,
  360. DeleteAssociatedDeviceByAccountKeyCallback callback,
  361. bool footprints_removal_success) {
  362. bool saved_device_registry_removal_success =
  363. saved_device_registry_->DeleteAccountKey(account_key);
  364. QP_LOG(INFO) << __func__ << ": Device removal: from Footprints: "
  365. << footprints_removal_success << "; from SavedDeviceRegistry: "
  366. << saved_device_registry_removal_success;
  367. std::move(callback).Run(/*success=*/footprints_removal_success &&
  368. saved_device_registry_removal_success);
  369. }
  370. void FastPairRepositoryImpl::FetchDeviceImages(scoped_refptr<Device> device) {
  371. QP_LOG(INFO) << __func__ << ": Fetching device images for model ID "
  372. << device->metadata_id;
  373. // Save a record of the device ID -> model ID for this device so that we can
  374. // display images for device objects that lack a model ID, such as
  375. // device::BluetoothDevice.
  376. if (!device_id_map_->SaveModelIdForDevice(device)) {
  377. QP_LOG(WARNING) << __func__
  378. << ": Unable to save device ID -> model ID"
  379. " mapping for model ID "
  380. << device->metadata_id;
  381. }
  382. GetDeviceMetadata(
  383. device->metadata_id,
  384. base::BindOnce(&FastPairRepositoryImpl::CompleteFetchDeviceImages,
  385. weak_ptr_factory_.GetWeakPtr(), device->metadata_id));
  386. }
  387. void FastPairRepositoryImpl::CompleteFetchDeviceImages(
  388. const std::string& hex_model_id,
  389. DeviceMetadata* device_metadata,
  390. bool has_retryable_error) {
  391. if (!device_metadata) {
  392. QP_LOG(WARNING) << __func__ << ": No metadata available for "
  393. << hex_model_id;
  394. return;
  395. }
  396. QP_LOG(INFO) << __func__
  397. << ": Completing fetching device images for model ID "
  398. << hex_model_id;
  399. device_image_store_->FetchDeviceImages(hex_model_id, device_metadata,
  400. base::DoNothing());
  401. }
  402. bool FastPairRepositoryImpl::PersistDeviceImages(scoped_refptr<Device> device) {
  403. QP_LOG(INFO) << __func__ << ": Persisting device images for model ID "
  404. << device->metadata_id;
  405. if (!device_id_map_->PersistRecordsForDevice(device)) {
  406. QP_LOG(WARNING) << __func__
  407. << ": Unable to persist address -> model ID"
  408. " mapping for model ID "
  409. << device->metadata_id;
  410. }
  411. return device_image_store_->PersistDeviceImages(device->metadata_id);
  412. }
  413. bool FastPairRepositoryImpl::EvictDeviceImages(
  414. const device::BluetoothDevice* device) {
  415. const std::string device_id = device->GetIdentifier();
  416. absl::optional<const std::string> hex_model_id =
  417. device_id_map_->GetModelIdForDeviceId(device_id);
  418. if (!hex_model_id)
  419. return false;
  420. device_id_map_->EvictDeviceIdRecord(device_id);
  421. // Before evicting images, check if other device IDs map to this model ID.
  422. if (device_id_map_->HasPersistedRecordsForModelId(hex_model_id.value()))
  423. return false;
  424. return device_image_store_->EvictDeviceImages(hex_model_id.value());
  425. }
  426. absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
  427. FastPairRepositoryImpl::GetImagesForDevice(const std::string& device_id) {
  428. absl::optional<const std::string> hex_model_id =
  429. device_id_map_->GetModelIdForDeviceId(device_id);
  430. if (!hex_model_id) {
  431. return absl::nullopt;
  432. }
  433. return device_image_store_->GetImagesForDeviceModel(hex_model_id.value());
  434. }
  435. } // namespace quick_pair
  436. } // namespace ash