1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
|
// Copyright 2018 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_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_REGISTRY_CACHE_H_
#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_REGISTRY_CACHE_H_
#include <functional>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/sequence_checker.h"
#include "components/account_id/account_id.h"
#include "components/services/app_service/public/cpp/app.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
namespace apps {
// An in-memory cache of all the metadata about installed apps known to App
// Service. Can be queried synchronously for information about the current
// state, and can be observed to receive updates about changes to that app
// state.
//
// AppServiceProxy sees a stream of `apps::AppPtr` "deltas", or changes in app
// state, received from publishers. This cache stores the "sum" of those
// previous deltas. When a new delta is received, observers are presented with
// an `apps:::AppUpdate` containing information about what has changed, and
// then the new delta is "added" to the stored state.
//
// This class is not thread-safe.
//
// See components/services/app_service/README.md for more details.
class COMPONENT_EXPORT(APP_UPDATE) AppRegistryCache {
public:
// Observer for changes to app state in the AppRegistryCache.
class COMPONENT_EXPORT(APP_UPDATE) Observer : public base::CheckedObserver {
public:
Observer(const Observer&) = delete;
Observer& operator=(const Observer&) = delete;
// Called whenever AppRegistryCache receives an update for any app. `update`
// exposes the latest field values and whether they have changed in this
// update (as per the docs on `apps::AppUpdate`). The `update` argument
// shouldn't be accessed after OnAppUpdate returns.
virtual void OnAppUpdate(const AppUpdate& update) {}
// Called when the AppRegistryCache first receives a set of apps for
// `app_type`. This is called after reading from the AppStorage file.
// Note that this will not be called for app types initialized prior to this
// observer being registered. Observers should call
// AppRegistryCache::InitializedAppTypes() at the time of starting
// observation to get a set of the app types which have been initialized.
virtual void OnAppTypeInitialized(apps::AppType app_type) {}
// Called whenever AppRegistryCache.OnApps is called with
// `should_notify_initialized` is true, when the publisher publishes apps
// for the first time after the system startup. AppRegistryCache's internal
// variables haven't been updated, so `states_` and `deltas_in_progress_`
// are having the old app info, not include any new app info in `delta`.
//
// Please use OnAppTypeInitialized if possible.
virtual void OnAppTypePublishing(const std::vector<AppPtr>& deltas,
apps::AppType app_type) {}
// Called when the AppRegistryCache object (the thing that this observer
// observes) will be destroyed. In response, the observer, `this`, should
// call "cache->RemoveObserver(this)", whether directly or indirectly (e.g.
// via base::ScopedObservation::Reset).
virtual void OnAppRegistryCacheWillBeDestroyed(AppRegistryCache* cache) = 0;
protected:
Observer();
~Observer() override;
};
AppRegistryCache();
AppRegistryCache(const AppRegistryCache&) = delete;
AppRegistryCache& operator=(const AppRegistryCache&) = delete;
~AppRegistryCache();
// Prefer using a base::ScopedObservation for idiomatic observer behavior.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
AppType GetAppType(const std::string& app_id);
std::vector<AppPtr> GetAllApps();
void SetAccountId(const AccountId& account_id);
// Calls f, a void-returning function whose arguments are (const
// apps::AppUpdate&), on each app in the cache.
//
// f's argument is an apps::AppUpdate instead of an apps::AppPtr so
// that callers can more easily share code with Observer::OnAppUpdate (which
// also takes an apps::AppUpdate), and an apps::AppUpdate also has a
// StateIsNull method.
//
// The apps::AppUpdate argument to f shouldn't be accessed after f returns.
//
// f must be synchronous, and if it asynchronously calls ForEachApp again,
// it's not guaranteed to see a consistent state.
template <typename FunctionType>
void ForEachApp(FunctionType f) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
for (const auto& s_iter : states_) {
const App* state = s_iter.second.get();
auto d_iter = deltas_in_progress_.find(s_iter.first);
const App* delta =
(d_iter != deltas_in_progress_.end()) ? d_iter->second : nullptr;
f(AppUpdate(state, delta, account_id_));
}
for (const auto& d_iter : deltas_in_progress_) {
const App* delta = d_iter.second;
if (base::Contains(states_, d_iter.first)) {
continue;
}
f(AppUpdate(nullptr, delta, account_id_));
}
}
// Returns an `apps::AppUpdate` corresponding to the app in the cache with the
// given `app_id`, or `nullopt` if there is not such an app.
//
// The `apps::AppUpdate` view may dangle when the state of the cache changes,
// and should not be accessed after this happens.
std::optional<AppUpdate> GetAppUpdate(std::string_view app_id) const;
// Calls f, a void-returning function whose arguments are (const
// apps::AppUpdate&), on the app in the cache with the given app_id. It will
// return true (and call f) if there is such an app, otherwise it will return
// false (and not call f). The AppUpdate argument to f has the same semantics
// as for ForEachApp, above.
//
// f must be synchronous, and if it asynchronously calls ForOneApp again,
// it's not guaranteed to see a consistent state.
template <typename FunctionType>
bool ForOneApp(const std::string& app_id, FunctionType f) const {
std::optional<AppUpdate> app_update = GetAppUpdate(app_id);
if (app_update.has_value()) {
f(*app_update);
return true;
}
return false;
}
// Returns the set of app types that have so far been initialized.
const std::set<AppType>& InitializedAppTypes() const;
// Returns true after the initialization of `app_type` is done and the apps of
// `app_type` is ready in AppRegistryCache.
//
// Note: The app publisher and app platform might be not ready yet when
// IsAppTypeInitialized returns true.
//
// The initialization is considered done when the apps for `app_type` have
// been added to AppRegistryCache, when `InitApps` is called by AppStorage or
// when `should_notify_initialized` is set as true by the publisher of
// `app_type`.
bool IsAppTypeInitialized(AppType app_type) const;
// Returns true after both the publisher and the app platform of `app_type`
// are ready.
//
// Note: apps of `app_type` might haven't been added to AppRegistryCache when
// IsAppTypePublished returns true.
//
// The app type published is considered true when the publisher of `app_type`
// publishes apps when `should_notify_initialized` is set as true.
//
// When apps of `app_type` are initialized and added to AppRegistryCache, the
// publisher may be not ready yet, so IsAppTypeInitialized could return true,
// but IsAppTypePublished could be false. Only when the publisher publishes
// apps with `should_notify_initialized` as true, IsAppTypePublished returns
// true.
//
// Please use IsAppTypeInitialized if possible. This interface can be used to
// check whether the publisher and the app platform for `app_type` are ready
// for `app_type`.
bool IsAppTypePublished(AppType app_type) const;
// Returns true if the cache contains an app with id `app_id` whose
// `Readiness()` corresponds to an installed state.
bool IsAppInstalled(const std::string& app_id) const;
// Clears all apps from the cache.
void ReinitializeForTesting();
// Please use AppServiceProxy::OnApps if possible. This method is used to
// tests without Profile, e.g. unittests.
void OnAppsForTesting(std::vector<AppPtr> deltas,
apps::AppType app_type,
bool should_notify_initialized);
private:
friend class AppRegistryCacheTest;
friend class AppRegistryCacheWrapperTest;
friend class PublisherTest;
friend class AppStorage;
friend class FakeAppStorage;
friend class AppStorageTest;
friend class AppServiceProxyAsh;
friend class AppServiceProxyBase;
// Called by AppServiceProxy::OnApps when publishers publish changes on apps,
// to notifies all observers of state-and-delta AppUpdate's and then merges
// the cached states with the deltas. If `should_notify_initialized` is true,
// notify observers `app_type` has been initialized by calling
// `OnAppTypeInitialized`.
//
// Please use AppServiceProxy::OnApps if possible. For tests without Profile,
// e.g. unittests, please use OnAppsForTesting.
void OnApps(std::vector<AppPtr> deltas,
apps::AppType app_type,
bool should_notify_initialized);
// Notifies all observers of state-and-delta AppUpdate's (the state comes
// from the internal cache, the delta comes from the argument) and then
// merges the cached states with the deltas. This interface can be used to
// update apps for multiple app types, and it won't notify observers the
// initialized status.
//
// Notification and merging might be delayed until after OnApps returns. For
// example, suppose that the initial set of states is (a0, b0, c0) for three
// app_id's ("a", "b", "c"). Now suppose OnApps is called with two updates
// (b1, c1), and when notified of b1, an observer calls OnApps again with
// (c2, d2). The c1 delta should be processed before the c2 delta, as it was
// sent first: c2 should be merged (with "newest wins" semantics) onto c1 and
// not vice versa. This means that processing c2 (scheduled by the second
// OnApps call) should wait until the first OnApps call has finished
// processing b1 (and then c1), which means that processing c2 is delayed
// until after the second OnApps call returns.
//
// The callee will consume the deltas. An apps::AppPtr has the ownership
// semantics of a unique_ptr, and will be deleted when out of scope. The
// caller presumably calls OnApps(std::move(deltas)).
void OnApps(std::vector<AppPtr> deltas);
void DoOnApps(std::vector<AppPtr> deltas);
// Notifies all observers that apps of `app_type` have been initialized.
void InitApps(apps::AppType app_type);
void OnAppTypeInitialized();
base::ObserverList<Observer> observers_;
// Maps from app_id to the latest state: the "sum" of all previous deltas.
std::map<std::string, AppPtr, std::less<>> states_;
// Track the deltas being processed or are about to be processed by OnApps.
// They are separate to manage the "notification and merging might be delayed
// until after OnApps returns" concern described above.
//
// OnApps calls DoOnApps zero or more times. If we're nested, so that there's
// multiple OnApps call to this AppRegistryCache in the call stack, the
// deeper OnApps call simply adds work to deltas_pending_ and returns
// without calling DoOnApps. If we're not nested, OnApps calls DoOnApps one or
// more times; "more times" happens if DoOnApps notifying observers leads to
// more OnApps calls that enqueue deltas_pending_ work. The
// deltas_in_progress_ map (keyed by app_id) contains those deltas being
// considered by DoOnApps.
//
// Nested OnApps calls are expected to be rare (but still dealt with
// sensibly). In the typical case, OnApps should call DoOnApps exactly once,
// and deltas_pending_ will stay empty.
std::map<std::string, raw_ptr<App, CtnExperimental>, std::less<>> deltas_in_progress_;
std::vector<AppPtr> deltas_pending_;
// Saves app types which will finish initialization, and OnAppTypeInitialized
// will be called to notify observers.
std::set<AppType> in_progress_initialized_app_types_;
// Saves app types which have finished initialization, and
// OnAppTypeInitialized has been called to notify observers.
std::set<AppType> initialized_app_types_;
// Saves app types which have been published by the publisher, and
// OnAppTypePublished has been called to notify observers.
std::set<AppType> published_app_types_;
AccountId account_id_;
SEQUENCE_CHECKER(my_sequence_checker_);
};
} // namespace apps
#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_APP_REGISTRY_CACHE_H_
|