File: firmware_update_manager.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (437 lines) | stat: -rw-r--r-- 15,581 bytes parent folder | download | duplicates (7)
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
// Copyright 2021 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_FWUPD_FIRMWARE_UPDATE_MANAGER_H_
#define CHROMEOS_ASH_COMPONENTS_FWUPD_FIRMWARE_UPDATE_MANAGER_H_

#include <optional>
#include <string>

#include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/task/sequenced_task_runner.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_client.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_device.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_properties.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_request.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_update.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote_set.h"

namespace network {

class SimpleURLLoader;

}  // namespace network

namespace ash {

// State of the fwupd daemon. Enum defined here:
// https://github.com/fwupd/fwupd/blob/4389f9f913588edae7243a8dbed88ce3788c8bc2/libfwupd/fwupd-enums.h
// Keep in sync with corresponding enum in tools/metrics/histograms/enums.xml.
enum class FwupdStatus {
  kUnknown,
  kIdle,
  kLoading,
  kDecompressing,
  kDeviceRestart,
  kDeviceWrite,
  kDeviceVerify,
  kScheduling,
  kDownloading,
  kDeviceRead,
  kDeviceErase,
  kWaitingForAuth,
  kDeviceBusy,
  kShutdown,
  kWaitingForUser,
  kMaxValue = kWaitingForUser,
};

// Used in histograms. Keep in sync with FirmwareUpdateMethodResult in
// tools/metrics/histograms/metadata/chromeos/enums.xml.
enum class MethodResult {
  kSuccess = 0,
  // DEPRECATED: kInstallFailed = 1,
  kFailedToCreateUpdateDirectory = 2,
  // DEPRECATED: kInvalidDestinationFile = 3,
  kInvalidFile = 4,
  kFailedToDownloadToFile = 5,
  kFailedToCreatePatchFile = 6,
  kEmptyPatchFile = 7,
  kInvalidPatchFileUri = 8,
  kInvalidPatchFile = 9,
  kInstallFailedTimeout = 10,
  kFailedToGetFirmwareFilename = 11,

  // All Install Errors returned by fwupd dbus signal
  // These errors are consistent with
  // /chromeos/ash/components/dbus/fwupd/fwupd_client.h
  //
  // Starting values from 100 to keep the Fwupd Error message contiguous in case
  // more error names are added.
  kInternalError = 100,
  kVersionNewerError = 101,
  kVersionSameError = 102,
  kAlreadyPendingError = 103,
  kAuthFailedError = 104,
  kReadError = 105,
  kWriteError = 106,
  kInvalidFileError = 107,
  kNotFoundError = 108,
  kNothingToDoError = 109,
  kNotSupportedError = 110,
  kSignatureInvalidError = 111,
  kAcPowerRequiredError = 112,
  kPermissionDeniedError = 113,
  kBrokenSystemError = 114,
  kBatteryLevelTooLowError = 115,
  kNeedsUserActionError = 116,
  kAuthExpiredError = 117,
  kUnknownError = 118,
  kMaxValue = kUnknownError,
};

// FirmwareUpdateManager contains all logic that runs the firmware update SWA.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_FWUPD) FirmwareUpdateManager
    : public FwupdClient::Observer,
      public firmware_update::mojom::UpdateProvider,
      public firmware_update::mojom::InstallController,
      public firmware_update::mojom::SystemUtils,
      public NetworkStateHandlerObserver {
 public:
  enum class Source {
    kUI = 0,
    kStartup = 1,
    kUSBChange = 2,
    kInstallComplete = 3,
    kNetworkChange = 4,
    kMaxValue = kNetworkChange,
  };

  FirmwareUpdateManager();
  FirmwareUpdateManager(const FirmwareUpdateManager&) = delete;
  FirmwareUpdateManager& operator=(const FirmwareUpdateManager&) = delete;
  ~FirmwareUpdateManager() override;

  class Observer : public base::CheckedObserver {
   public:
    ~Observer() override = default;

    // Called to notify observers, primarily notification controllers, that a
    // critical firmware update is available.
    virtual void OnFirmwareUpdateReceived() = 0;
  };

  // Returns true if the global instance is initialized.
  static bool IsInitialized();

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  // firmware_update::mojom::UpdateProvider
  void ObservePeripheralUpdates(
      mojo::PendingRemote<firmware_update::mojom::UpdateObserver> observer)
      override;

  void PrepareForUpdate(const std::string& device_id,
                        PrepareForUpdateCallback callback) override;

  void FetchInProgressUpdate(FetchInProgressUpdateCallback callback) override;

  // firmware_update::mojom::InstallController
  void BeginUpdate(const std::string& device_id,
                   const base::FilePath& filepath) override;

  void AddDeviceRequestObserver(
      mojo::PendingRemote<firmware_update::mojom::DeviceRequestObserver>
          observer) override;

  void AddUpdateProgressObserver(
      mojo::PendingRemote<firmware_update::mojom::UpdateProgressObserver>
          observer) override;

  // Gets the global instance pointer.
  static FirmwareUpdateManager* Get();

  // Gets the number of cached updates.
  size_t GetUpdateCount() { return updates_.size(); }

  // FwupdClient::Observer:
  // When the fwupd DBus client gets a response with devices from fwupd,
  // it calls this function and passes the response.
  void OnDeviceListResponse(FwupdDeviceList* devices) override;

  void OnDeviceRequestResponse(FwupdRequest request) override;

  // When the fwupd DBus client gets a response with updates from fwupd,
  // it calls this function and passes the response.
  void OnUpdateListResponse(const std::string& device_id,
                            FwupdUpdateList* updates) override;
  // TODO(jimmyxgong): Implement this function to send property updates via
  // mojo.
  void OnPropertiesChangedResponse(FwupdProperties* properties) override;

  // Query all updates for all devices.
  void RequestAllUpdates(Source source);

  // NetworkStateHandlerObserver:
  void DefaultNetworkChanged(const NetworkState* network) override;

  void BindInterface(
      mojo::PendingReceiver<firmware_update::mojom::UpdateProvider>
          pending_receiver);

  void set_should_show_notification_for_test(bool show_notification) {
    should_show_notification_for_test_ = show_notification;
  }

  void set_refresh_remote_for_testing(bool for_testing) {
    refresh_remote_for_testing_ = for_testing;
  }

  // firmware_update::mojom::SystemUtils
  void Restart() override;

  void BindInterface(mojo::PendingReceiver<firmware_update::mojom::SystemUtils>
                         pending_receiver);

 protected:
  friend class FirmwareUpdateManagerTest;
  // Temporary auxiliary variables for testing.
  // TODO(swifton): Replace with mock observers.
  int on_device_list_response_count_for_testing_ = 0;
  int on_update_list_response_count_for_testing_ = 0;

 private:
  friend class FirmwareUpdateManagerTest;
  // Query the fwupd DBus client for currently connected devices.
  void RequestDevices();

  // Query the fwupd DBus client for updates for a certain device.
  void RequestUpdates(const std::string& device_id);

  typedef base::OnceCallback<void(MethodResult)> MethodCallback;

  // Download and prepare the install file for a specific device.
  void StartInstall(const std::string& device_id,
                    const base::FilePath& filepath,
                    MethodCallback callback);

  // Callback handler after fetching the file.
  void OnGetFile(const std::string& device_id,
                 FirmwareInstallOptions options,
                 MethodCallback callback,
                 base::File file);

  // Query the fwupd DBus client to install an update for a certain device.
  void InstallUpdate(const std::string& device_id,
                     FirmwareInstallOptions options,
                     MethodCallback callback,
                     base::File patch_file);

  // Response from fwupd DBus client InstallUpdate call.
  void OnInstallResponse(MethodCallback callback, FwupdDbusResult result);

  // InstallComplete will be called exactly once with a result when an install
  // attempt succeeds or fails for any reason.
  void InstallComplete(MethodResult result);

  void CreateLocalPatchFile(const base::FilePath& cache_path,
                            const std::string& device_id,
                            const base::FilePath& filepath,
                            MethodCallback callback,
                            bool create_dir_success);

  void MaybeDownloadFileToInternal(const base::FilePath& patch_path,
                                   const std::string& device_id,
                                   const base::FilePath& filepath,
                                   MethodCallback callback,
                                   bool write_file_success);

  void DownloadFileToInternal(const base::FilePath& patch_path,
                              const std::string& device_id,
                              const base::FilePath& filepath,
                              MethodCallback callback);

  void OnUrlDownloadedToFile(
      const std::string& device_id,
      std::unique_ptr<network::SimpleURLLoader> simple_loader,
      MethodCallback callback,
      base::FilePath download_path);

  // If refresh remote is allowed and call RefreshRemote otherwise continue with
  // RequestUpdates()
  void MaybeRefreshRemote(bool refresh_allowed);

  using DownloadCompleteCallback =
      base::OnceCallback<void(base::FilePath, base::File)>;

  // Refresh LVFS remote metadata by downloading the required files and calling
  // UpdateMetadata dbus function.
  void RefreshRemote();

  void CreateTempFileAndDownload(base::FilePath local_file,
                                 std::string download_filename,
                                 DownloadCompleteCallback callback,
                                 bool create_dir_success);

  void DownloadLvfsMirrorFile(std::string filename,
                              base::FilePath download_filepath,
                              DownloadCompleteCallback callback,
                              bool write_file_success);

  void GetFileDescriptor(
      std::unique_ptr<network::SimpleURLLoader> simple_loader,
      DownloadCompleteCallback callback,
      base::FilePath download_path);

  void OnGetChecksumFile(base::FilePath checksum_filepath,
                         base::File checksum_file);

  void GetFirmwareFilename(std::string file_contents);

  void TriggerDownloadOfFirmwareFile(std::string firmware_filename);

  // Call UpdateMetadata dbus api using `checksum_file_` and `firmware_file`.
  void UpdateMetadata(base::FilePath firmware_filepath,
                      base::File firmware_file);

  void OnUpdateMetadataResponse(FwupdDbusResult result);

  // RefreshRemoteComplete will be called exactly once with a result when an
  // attempt to refresh lvfs remote succeeds or fails for any reason.
  // Then continue requesting devices.
  void RefreshRemoteComplete(MethodResult result);

  // Notifies observers registered with ObservePeripheralUpdates() the current
  // list of devices with pending updates (if any).
  void NotifyUpdateListObservers();

  bool HasPendingUpdates();

  void set_fake_url_for_testing(const std::string& fake_url) {
    fake_url_for_testing_ = fake_url;
  }

  // Resets the mojo::Receiver |install_controller_receiver_|
  // and |update_progress_observer_|.
  void ResetInstallState();

  // Checks if any update in |updates_| is critical. If so,
  // a single notification is shown to the user.
  void ShowNotificationIfRequired();

  // Call to notify observers that a new notification is needed.
  void NotifyCriticalFirmwareUpdateReceived();

  // Records the # of devices found at startup and whenever the device list
  // is refreshed.
  void RecordDeviceMetrics(int num_devices);

  // Records the # of updates found at startup and whenever the update list
  // is refreshed.
  void RecordUpdateMetrics();

  int GetNumCriticalUpdates();

  // Gets /tmp directory path to store downloaded files.
  const base::FilePath GetCacheDirPath();

  // Map of a device ID to `FwupdDevice` which is waiting for the list
  // of updates.
  base::flat_map<std::string, FwupdDevice> devices_pending_update_;

  // Set of device IDs with critical updates that we've already shown a
  // notification for.
  base::flat_set<std::string> devices_already_notified_;

  // List of all available updates. If `devices_pending_update_` is not
  // empty then this list is not yet complete.
  std::vector<firmware_update::mojom::FirmwareUpdatePtr> updates_;

  // Only used for testing if StartInstall() queries to a fake URL.
  std::string fake_url_for_testing_;

  // The device update that is currently inflight.
  firmware_update::mojom::FirmwareUpdatePtr inflight_update_;

  // The most recent FwupdStatus, used for the purpose of recording metrics.
  FwupdStatus last_fwupd_status_ = FwupdStatus::kUnknown;

  // The most recent DeviceRequest, used for the purpose of recording metrics.
  firmware_update::mojom::DeviceRequestPtr last_device_request_ = nullptr;

  // Timestamp of when the last device request began. Used to calculate a
  // duration for metrics.
  std::optional<base::Time> last_request_started_timestamp_;

  // Used to show the firmware update notification and to determine which
  // metric to fire (Startup/Refresh).
  bool is_first_response_ = true;

  // Whether or not fetching updates in inflight.
  bool is_fetching_updates_ = false;

  // Whether Refresh Remote has been requested and pending successful
  // completion.
  bool is_refresh_pending_ = false;

  // Checksum and firmware paths and File objects are held temporarily during
  // download, and are used for cleanup which must be done on task_runner_.
  base::FilePath checksum_filepath_;
  base::FilePath firmware_filepath_;
  base::File checksum_file_;
  base::File firmware_file_;

  // Used only for testing to force notification to appear.
  bool should_show_notification_for_test_ = false;

  // Used only for testing to trigger RefreshRemote and create file in random
  // directory to avoid flakiness
  bool refresh_remote_for_testing_ = false;

  // Remotes for tracking observers that will be notified of changes to the
  // list of firmware updates.
  mojo::RemoteSet<firmware_update::mojom::UpdateObserver>
      update_list_observers_;

  // Remote for tracking observer that will be notified of incoming
  // DeviceRequests.
  mojo::Remote<firmware_update::mojom::DeviceRequestObserver>
      device_request_observer_;

  // Remote for tracking observer that will be notified of changes to
  // the in-progress update.
  mojo::Remote<firmware_update::mojom::UpdateProgressObserver>
      update_progress_observer_;

  base::ObserverList<Observer> observer_list_;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  mojo::Receiver<firmware_update::mojom::UpdateProvider> receiver_{this};

  mojo::Receiver<firmware_update::mojom::InstallController>
      install_controller_receiver_{this};

  mojo::Receiver<firmware_update::mojom::SystemUtils> system_utils_receiver_{
      this};

  base::WeakPtrFactory<FirmwareUpdateManager> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROMEOS_ASH_COMPONENTS_FWUPD_FIRMWARE_UPDATE_MANAGER_H_