// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_ASH_COMPONENTS_NEARBY_PRESENCE_CREDENTIALS_NEARBY_PRESENCE_CREDENTIAL_MANAGER_IMPL_H_
#define CHROMEOS_ASH_COMPONENTS_NEARBY_PRESENCE_CREDENTIALS_NEARBY_PRESENCE_CREDENTIAL_MANAGER_IMPL_H_
#include "base/memory/raw_ref.h"
#include "chromeos/ash/components/nearby/presence/credentials/nearby_presence_credential_manager.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/nearby/common/client/nearby_http_result.h"
#include "chromeos/ash/components/nearby/presence/metrics/nearby_presence_metrics.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
class PrefService;
namespace signin {
class IdentityManager;
} // namespace signin
namespace network {
class SharedURLLoaderFactory;
} // namespace network
namespace ash::nearby {
class NearbyScheduler;
} // namespace ash::nearby
namespace ash::nearby::proto {
class UpdateDeviceResponse;
class ListSharedCredentialsResponse;
} // namespace ash::nearby::proto
namespace nearby::internal {
class SharedCredential;
} // namespace nearby::internal
namespace ash::nearby::presence {
class LocalDeviceDataProvider;
class NearbyPresenceServerClient;
// This class is a singleton, and callers can only create one instance via
// the Creator.
class NearbyPresenceCredentialManagerImpl
: public NearbyPresenceCredentialManager {
public:
// A creator class for building a CredentialManager instance. The `Create`
// function is async in order to ensure a ready CredentialManager is returned
// to callers. A ready CredentialManager can be created by one of two flows:
//
// Case 1: First Time Registration (the case where this is the first time
// Nearby Presence is being run on this ChromeOS device). Before the
// CredentialManager is returned to callers, it completes the first time
// server registration flow to register with the Nearby Presence server,
// generate and upload local device credentials, and download remote devices'
// credentials.
//
// Case 2: Other cases (most common path): Before the CredentialManager is
// returned to callers, it sets the device metadata in the NP library over
// the mojo pipe.
//
// This class expects and enforces that it will only be used once to create a
// single CredentialManager instance during its lifetime.
class Creator {
public:
using CreateCallback = base::OnceCallback<void(
std::unique_ptr<NearbyPresenceCredentialManager>)>;
virtual ~Creator();
Creator(Creator&) = delete;
Creator& operator=(Creator&) = delete;
static Creator* Get();
static void SetNextCredentialManagerInstanceForTesting(
std::unique_ptr<NearbyPresenceCredentialManager> credential_manager);
void Create(
PrefService* pref_service,
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const mojo::SharedRemote<mojom::NearbyPresence>& nearby_presence,
CreateCallback on_created);
protected:
Creator();
// For unit tests only. |local_device_data_provider| parameter is used to
// inject a FakeLocalDeviceDataProvider.
virtual void Create(
PrefService* pref_service,
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const mojo::SharedRemote<mojom::NearbyPresence>& nearby_presence,
std::unique_ptr<LocalDeviceDataProvider> local_device_data_provider,
CreateCallback on_created);
bool has_credential_manager_been_created_ = false;
private:
friend class base::NoDestructor<Creator>;
void OnCredentialManagerRegistered(bool success);
void OnCredentialManagerInitialized();
// While a new CredentialManager is being initialized the factory retains a
// reference to it. After initialization is complete |on_created_|
// is run. If |credential_manager_under_initialization_| is set before the
// `Create` function for testing using `SetCredentialManagerForTesting`,
// then it will be returned on the callback, skipping all of the
// initialization flow, as long as the
// `CredentialManager::IsLocalDeviceRegistered` returns true.
std::unique_ptr<NearbyPresenceCredentialManager>
credential_manager_under_initialization_;
CreateCallback on_created_;
};
NearbyPresenceCredentialManagerImpl(NearbyPresenceCredentialManagerImpl&) =
delete;
NearbyPresenceCredentialManagerImpl& operator=(
NearbyPresenceCredentialManagerImpl&) = delete;
~NearbyPresenceCredentialManagerImpl() override;
// NearbyPresenceCredentialManager:
bool IsLocalDeviceRegistered() override;
void RegisterPresence(
base::OnceCallback<void(bool)> on_registered_callback) override;
void UpdateCredentials() override;
void InitializeDeviceMetadata(
base::OnceClosure on_metadata_initialized_callback) override;
protected:
NearbyPresenceCredentialManagerImpl(
PrefService* pref_service,
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const mojo::SharedRemote<mojom::NearbyPresence>& nearby_presence,
std::unique_ptr<LocalDeviceDataProvider> local_device_data_provider);
private:
void StartFirstTimeRegistration();
void OnFirstTimeRegistrationComplete(
metrics::FirstTimeRegistrationResult result);
// Callbacks for server registration UpdateDevice RPC via
// |RegisterPresence|.
void HandleFirstTimeRegistrationTimeout();
void HandleFirstTimeRegistrationFailure(ash::nearby::NearbyHttpResult result);
void OnRegistrationRpcSuccess(
base::TimeTicks registration_request_start_time,
const ash::nearby::proto::UpdateDeviceResponse& response);
void OnRegistrationRpcFailure(ash::nearby::NearbyHttpError error);
// Callback for credential generation in the NP library during the first
// time registration flow.
//
// TODO(b/286594539): Revisit this flow when there are additional triggers to
// regenerate credentials outside the first time flow stemming from metadata
// changes.
void OnFirstTimeCredentialsGenerated(
std::vector<mojom::SharedCredentialPtr> shared_credentials,
mojo_base::mojom::AbslStatusCode status);
// Callback for first time remote credential saving in the NP library.
void OnFirstTimeRemoteCredentialsSaved(
mojo_base::mojom::AbslStatusCode status);
// Callbacks for credential upload/download during first time
// server registration and daily syncs.
void OnFirstTimeCredentialsUpload(bool success);
void OnFirstTimeCredentialsDownload(
std::vector<::nearby::internal::SharedCredential> credentials,
bool success);
void StartDailySync();
// Callback for fetching local device credentials from the NP library during
// daily syncs.
void OnGetLocalSharedCredentials(
std::vector<mojom::SharedCredentialPtr> shared_credentials,
mojo_base::mojom::AbslStatusCode status);
// Callbacks for uploading/downloading credentials as part of the
// daily syncs.
void OnDailySyncCredentialUpload(bool success);
void OnDailySyncCredentialDownload(
std::vector<::nearby::internal::SharedCredential> credentials,
bool success);
// Callback for remote credential saving in the NP library that is part of
// the daily sync flow.
void OnDailySyncRemoteCredentialsSaved(
mojo_base::mojom::AbslStatusCode status);
// Helper functions to trigger uploading credentials in the NP server. The
// helper functions are used for first time server registration to upload
// newly generated credentials, daily syncs with the server to upload
// credentials if they have changed. These helper functions are also used in
// `UpdateCredentials` flows.
//
// They take a repeating callback because `UploadCredentials()` and
// `DownloadCredentials()` must be bound as a RepeatingCallback itself as a
// task in a NearbyScheduler.
void ScheduleUploadCredentials(
std::vector<::nearby::internal::SharedCredential>
proto_shared_credentials,
base::RepeatingCallback<void(bool)> on_upload);
void ScheduleDownloadCredentials(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
on_download);
void UploadCredentials(
std::vector<::nearby::internal::SharedCredential> credentials,
base::RepeatingCallback<void(bool)> upload_credentials_result_callback);
void HandleUploadCredentialsResult(
base::RepeatingCallback<void(bool)> upload_credentials_callback,
ash::nearby::NearbyHttpResult result);
void OnUploadCredentialsTimeout(
base::RepeatingCallback<void(bool)> upload_credentials_callback);
void OnUploadCredentialsSuccess(
base::RepeatingCallback<void(bool)> upload_credentials_callback,
base::TimeTicks upload_request_start_time,
const ash::nearby::proto::UpdateDeviceResponse& response);
void OnUploadCredentialsFailure(
base::RepeatingCallback<void(bool)> upload_credentials_callback,
ash::nearby::NearbyHttpError error);
void DownloadCredentials(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
download_credentials_result_callback);
void HandleDownloadCredentialsResult(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
download_credentials_result_callback,
ash::nearby::NearbyHttpResult result,
std::vector<::nearby::internal::SharedCredential> credentials);
void OnDownloadCredentialsTimeout(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
download_credentials_result_callback);
void OnDownloadCredentialsSuccess(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
download_credentials_result_callback,
base::TimeTicks download_request_start_time,
const ash::nearby::proto::ListSharedCredentialsResponse& response);
void OnDownloadCredentialsFailure(
base::RepeatingCallback<
void(std::vector<::nearby::internal::SharedCredential>, bool)>
download_credentials_result_callback,
ash::nearby::NearbyHttpError error);
const raw_ptr<PrefService> pref_service_ = nullptr;
const raw_ptr<signin::IdentityManager> identity_manager_ = nullptr;
// Constructed per RPC request, and destroyed on RPC response (server
// interaction completed). This field is reused by multiple RPCs during the
// lifetime of the NearbyPresenceCredentialManagerImpl object.
std::unique_ptr<NearbyPresenceServerClient> server_client_;
std::unique_ptr<LocalDeviceDataProvider> local_device_data_provider_;
base::OneShotTimer server_response_timer_;
const raw_ref<const mojo::SharedRemote<mojom::NearbyPresence>>
nearby_presence_;
const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Schedulers used to schedule immediate tasks to communicate with the
// server during the first time registration flow and daily credential sync
// flow.
std::unique_ptr<ash::nearby::NearbyScheduler> upload_on_demand_scheduler_;
std::unique_ptr<ash::nearby::NearbyScheduler> download_on_demand_scheduler_;
// Stores the number of current attempts for uploading credentials to the
// server. Increments on each attempt to upload credentials, and is reset to 0
// once the upload request is completed (either resulting in final
// success or failure).
int upload_credentials_attempts_needed_count_ = 0;
// Stores the number of current attempts for downloading credentials from the
// server. Increments on each attempt to download credentials, and is reset to
// 0 once the download request is completed (either resulting in final
// success or failure).
int download_credentials_attempts_needed_count_ = 0;
// Stores the number of current attempts for registered the local device with
// the Nearby Presence server. Increments on each attempt, and is reset to 0
// once the registration request is completed (either resulting in final
// success or failure).
int first_time_server_registration_attempts_needed_count_ = 0;
// Initialized during the first time registration flow kicked off in
// `RegisterPresence()`. Not expected to be a valid pointer unless used during
// the first time registration flow.
std::unique_ptr<ash::nearby::NearbyScheduler>
first_time_registration_on_demand_scheduler_;
// Scheduler used for daily credential syncs with the server. Every 24 hours,
// attempt to upload the local device's credentials and download/save
// remote devices' credentials.
std::unique_ptr<ash::nearby::NearbyScheduler>
daily_credential_sync_scheduler_;
bool is_daily_sync_in_progress_ = false;
// Stores the last success time of a daily sync to prevent slamming the
// server with requests to `UpdateCredentials()`.
std::optional<base::Time> last_daily_sync_success_time_;
// Stores a count of the number of requests to `UpdateCredentials()` made
// to match with a corresponding cool off period in between requests to
// prevent overwhelming the server.
int update_credential_request_count_ = 0;
// Callback to return the result of the first time registration. Not
// guaranteed to be a valid callback, as this is set only during first time
// registration flow via |RegisterPresence|.
base::OnceCallback<void(bool)> on_registered_callback_;
base::WeakPtrFactory<NearbyPresenceCredentialManagerImpl> weak_ptr_factory_{
this};
};
} // namespace ash::nearby::presence
#endif // CHROMEOS_ASH_COMPONENTS_NEARBY_PRESENCE_CREDENTIALS_NEARBY_PRESENCE_CREDENTIAL_MANAGER_IMPL_H_