File: app_registry_cache.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (309 lines) | stat: -rw-r--r-- 13,123 bytes parent folder | download | duplicates (6)
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_