| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_DATA_SHARING_INTERNAL_GROUP_DATA_MODEL_H_ |
| #define COMPONENTS_DATA_SHARING_INTERNAL_GROUP_DATA_MODEL_H_ |
| |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/observer_list_types.h" |
| #include "base/time/time.h" |
| #include "components/data_sharing/internal/collaboration_group_sync_bridge.h" |
| #include "components/data_sharing/internal/group_data_store.h" |
| #include "components/data_sharing/public/data_sharing_sdk_delegate.h" |
| #include "components/data_sharing/public/group_data.h" |
| #include "components/data_sharing/public/protocol/data_sharing_sdk.pb.h" |
| |
| class GaiaId; |
| |
| namespace data_sharing { |
| |
| // This class manages GroupData and ensures it is synchronized: |
| // * Provides in-memory and persistent storage for GroupData by incapsulating |
| // database that stores known GroupData. |
| // * Observes changes in CollaborationGroupSyncBridge and reflects them in |
| // cache/DB, retrieving data from SDK when needed. |
| class GroupDataModel : public CollaborationGroupSyncBridge::Observer { |
| public: |
| class Observer : public base::CheckedObserver { |
| public: |
| Observer() = default; |
| ~Observer() override = default; |
| |
| // Indicates that data is loaded from the disk, it can still be stale |
| // though. GetGroup() / GetAllGroups() returns no data prior to this call. |
| virtual void OnModelLoaded() = 0; |
| |
| virtual void OnGroupAdded(const GroupId& group_id, |
| const base::Time& event_time) = 0; |
| virtual void OnGroupUpdated(const GroupId& group_id, |
| const base::Time& event_time) = 0; |
| virtual void OnGroupDeleted(const GroupId& group_id, |
| const std::optional<GroupData>& group_data, |
| const base::Time& event_time) = 0; |
| |
| virtual void OnMemberAdded(const GroupId& group_id, |
| const GaiaId& member_gaia_id, |
| const base::Time& event_time) = 0; |
| virtual void OnMemberRemoved(const GroupId& group_id, |
| const GaiaId& member_gaia_id, |
| const base::Time& event_time) = 0; |
| virtual void OnSyncBridgeUpdateTypeChanged( |
| SyncBridgeUpdateType sync_bridge_update_type) = 0; |
| }; |
| |
| // `collaboration_group_sync_bridge` and `sdk_delegate` must not be null and |
| // must outlive `this`. |
| GroupDataModel(const base::FilePath& data_sharing_dir, |
| CollaborationGroupSyncBridge* collaboration_group_sync_bridge, |
| DataSharingSDKDelegate* sdk_delegate); |
| ~GroupDataModel() override; |
| |
| GroupDataModel(const GroupDataModel&) = delete; |
| GroupDataModel& operator=(const GroupDataModel&) = delete; |
| GroupDataModel(GroupDataModel&&) = delete; |
| GroupDataModel& operator=(GroupDataModel&&) = delete; |
| |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| // Indicates whether data is loaded from the disk, it can still be stale |
| // though. GetGroup() / GetAllGroups() returns no data prior as long as it is |
| // set to false. |
| bool IsModelLoaded() const; |
| |
| // Returns nullopt if the group is not (yet) stored locally or doesn't exist. |
| std::optional<GroupData> GetGroup(const GroupId& group_id) const; |
| // Groups are ordered by id. |
| std::set<GroupData> GetAllGroups() const; |
| // Returns nullopt if no data about the member is found. |
| std::optional<GroupMemberPartialData> GetPossiblyRemovedGroupMember( |
| const GroupId& group_id, |
| const GaiaId& member_gaia_id) const; |
| std::vector<GroupEvent> GetGroupEventsSinceStartup() const; |
| |
| // CollaborationGroupSyncBridge::Observer implementation. |
| void OnGroupsUpdated(const std::vector<GroupId>& added_group_ids, |
| const std::vector<GroupId>& updated_group_ids, |
| const std::vector<GroupId>& deleted_group_ids) override; |
| void OnCollaborationGroupSyncDataLoaded() override; |
| void OnSyncBridgeUpdateTypeChanged( |
| SyncBridgeUpdateType sync_bridge_update_type) override; |
| |
| GroupDataStore& GetGroupDataStoreForTesting(); |
| void SetGroupDataStoreLoadedCallbackForTesting( |
| base::OnceClosure db_loaded_callback); |
| |
| private: |
| void OnGroupDataStoreLoaded(GroupDataStore::DBInitStatus status); |
| |
| // Handles all known group changes, i.e. when data stored in |
| // `group_data_store_` is different from data in |
| // `collaboration_group_sync_bridge_`. |
| void ProcessGroupChanges(bool is_initial_load); |
| |
| void DoPeriodicPollingAndScheduleNext(); |
| void ScheduleNextPeriodicPolling(); |
| |
| // Asynchronously fetches data from the SDK. |
| void FetchGroupsFromSDK(const std::vector<GroupId>& added_or_updated_groups); |
| void OnBatchOfGroupsFetchedFromSDK( |
| const std::map<GroupId, VersionToken>& requested_groups_and_versions, |
| const base::Time& requested_at_timestamp, |
| const base::expected<data_sharing_pb::ReadGroupsResult, absl::Status>& |
| read_groups_result); |
| void FetchBatchOfGroupsFromSDK(const std::vector<GroupId>& batch); |
| void HandleBatchCompletion(); |
| |
| void NotifyObserversAboutChangedMembers(const GroupData& old_group_data, |
| const GroupData& new_group_data); |
| void MaybeRecordGroupEvent( |
| const GroupId& group_id, |
| GroupEvent::EventType event_type, |
| base::Time event_time, |
| std::optional<GaiaId> affected_member_gaia_id = std::nullopt); |
| |
| GroupDataStore group_data_store_; |
| bool is_group_data_store_loaded_ = false; |
| bool is_collaboration_group_bridge_loaded_ = false; |
| // TODO(crbug.com/370897286): Add test coverage for the scenarios addressed |
| // by the following two fields: when there is a race condition between two |
| // consecutive fetches (see crrev.com/c/5965993). |
| bool has_ongoing_group_fetch_ = false; |
| bool has_pending_changes_ = false; |
| |
| // Keeps track of the number of ReadGroups requests are currently in-flight. |
| int outstanding_batches_ = 0; |
| |
| std::vector<GroupEvent> group_events_since_startup_; |
| |
| raw_ptr<CollaborationGroupSyncBridge> collaboration_group_sync_bridge_; |
| raw_ptr<DataSharingSDKDelegate> sdk_delegate_; |
| |
| // Used only for tests to notify that GroupDataStore has been loaded (either |
| // successfully or unsuccessfully). |
| base::OnceClosure db_loaded_callback_; |
| |
| base::OneShotTimer next_periodic_polling_timer_; |
| |
| base::ObserverList<Observer> observers_; |
| |
| base::WeakPtrFactory<GroupDataModel> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace data_sharing |
| |
| #endif // COMPONENTS_DATA_SHARING_INTERNAL_GROUP_DATA_MODEL_H_ |