firmware_update_manager.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/components/fwupd/firmware_update_manager.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "ash/components/fwupd/histogram_util.h"
  8. #include "ash/public/cpp/fwupd_download_client.h"
  9. #include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
  10. #include "base/base_paths.h"
  11. #include "base/check_op.h"
  12. #include "base/containers/contains.h"
  13. #include "base/containers/fixed_flat_map.h"
  14. #include "base/files/file.h"
  15. #include "base/files/file_path.h"
  16. #include "base/files/file_util.h"
  17. #include "base/files/scoped_file.h"
  18. #include "base/logging.h"
  19. #include "base/path_service.h"
  20. #include "base/strings/string_util.h"
  21. #include "base/strings/utf_string_conversions.h"
  22. #include "base/task/task_traits.h"
  23. #include "base/task/thread_pool.h"
  24. #include "chromeos/ash/components/dbus/fwupd/fwupd_client.h"
  25. #include "crypto/sha2.h"
  26. #include "dbus/message.h"
  27. #include "mojo/public/cpp/bindings/pending_receiver.h"
  28. #include "mojo/public/cpp/bindings/receiver.h"
  29. #include "mojo/public/cpp/bindings/remote_set.h"
  30. #include "net/traffic_annotation/network_traffic_annotation.h"
  31. #include "services/network/public/cpp/resource_request.h"
  32. #include "services/network/public/cpp/shared_url_loader_factory.h"
  33. #include "services/network/public/cpp/simple_url_loader.h"
  34. #include "services/network/public/mojom/fetch_api.mojom.h"
  35. #include "services/network/public/mojom/url_response_head.mojom.h"
  36. #include "url/gurl.h"
  37. namespace ash {
  38. namespace {
  39. // State of the fwupd daemon. Enum defined here:
  40. // https://github.com/fwupd/fwupd/blob/4389f9f913588edae7243a8dbed88ce3788c8bc2/libfwupd/fwupd-enums.h
  41. enum class FwupdStatus {
  42. kUnknown,
  43. kIdle,
  44. kLoading,
  45. kDecompressing,
  46. kDeviceRestart,
  47. kDeviceWrite,
  48. kDeviceVerify,
  49. kScheduling,
  50. kDownloading,
  51. kDeviceRead,
  52. kDeviceErase,
  53. kWaitingForAuth,
  54. kDeviceBusy,
  55. kShutdown,
  56. };
  57. static constexpr auto FwupdStatusStringMap =
  58. base::MakeFixedFlatMap<FwupdStatus, const char*>(
  59. {{FwupdStatus::kUnknown, "Unknown state"},
  60. {FwupdStatus::kIdle, "Idle state"},
  61. {FwupdStatus::kLoading, "Loading a resource"},
  62. {FwupdStatus::kDecompressing, "Decompressing firmware"},
  63. {FwupdStatus::kDeviceRestart, "Restarting the device"},
  64. {FwupdStatus::kDeviceWrite, "Writing to a device"},
  65. {FwupdStatus::kDeviceVerify, "Verifying (reading) a device"},
  66. {FwupdStatus::kScheduling, "Scheduling an offline update"},
  67. {FwupdStatus::kDownloading, "A file is downloading"},
  68. {FwupdStatus::kDeviceRead, "Reading from a device"},
  69. {FwupdStatus::kDeviceErase, "Erasing a device"},
  70. {FwupdStatus::kWaitingForAuth, "Waiting for authentication"},
  71. {FwupdStatus::kDeviceBusy, "The device is busy"},
  72. {FwupdStatus::kShutdown, "The daemon is shutting down"}});
  73. const char* GetFwupdStatusString(FwupdStatus enum_val) {
  74. DCHECK(base::Contains(FwupdStatusStringMap, enum_val));
  75. return FwupdStatusStringMap.at(enum_val);
  76. }
  77. const char kBaseRootPath[] = "firmware-updates";
  78. const char kCachePath[] = "cache";
  79. const char kCabFileExtension[] = ".cab";
  80. const char kAllowedFilepathChars[] =
  81. "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-._";
  82. const char kHttpsComponent[] = "https:";
  83. const char kFileComponent[] = "file:";
  84. FirmwareUpdateManager* g_instance = nullptr;
  85. base::ScopedFD OpenFileAndGetFileDescriptor(base::FilePath download_path) {
  86. base::File dest_file(download_path,
  87. base::File::FLAG_OPEN | base::File::FLAG_READ);
  88. if (!dest_file.IsValid() || !base::PathExists(download_path)) {
  89. LOG(ERROR) << "Invalid destination file at path: " << download_path;
  90. firmware_update::metrics::EmitInstallResult(
  91. firmware_update::metrics::FirmwareUpdateInstallResult::
  92. kInvalidDestinationFile);
  93. return base::ScopedFD();
  94. }
  95. return base::ScopedFD(dest_file.TakePlatformFile());
  96. }
  97. base::File VerifyChecksum(base::File file, const std::string& checksum) {
  98. // Sha256 is 32 bytes, if it isn't Sha256 then return false.
  99. // The Sha256 string representation is 64 bytes, Sha256 is 32 bytes long.
  100. if (checksum.length() != crypto::kSHA256Length * 2) {
  101. return base::File();
  102. }
  103. const int64_t raw_file_length = file.GetLength();
  104. // Length of the file should not exceed int::max.
  105. if (raw_file_length > std::numeric_limits<int>::max()) {
  106. return base::File();
  107. }
  108. // Safe to truncate down to <int>.
  109. int file_length = raw_file_length;
  110. // Check checksum of the file.
  111. std::vector<char> buf(file_length);
  112. if (file.Read(0, buf.data(), file_length) != file_length) {
  113. return base::File();
  114. }
  115. const base::StringPiece contents(buf.data(), file_length);
  116. const std::string sha_contents = crypto::SHA256HashString(contents);
  117. const std::string encoded_sha = base::ToLowerASCII(
  118. base::HexEncode(sha_contents.data(), sha_contents.size()));
  119. if (encoded_sha != checksum) {
  120. LOG(ERROR) << "Wrong checksum, expected: " << checksum
  121. << " but got: " << encoded_sha;
  122. return base::File();
  123. }
  124. // Reset current pointer of file so that it can be read again.
  125. if (file.Seek(base::File::FROM_BEGIN, 0) != 0) {
  126. return base::File();
  127. }
  128. return file;
  129. }
  130. bool CreateDirIfNotExists(const base::FilePath& path) {
  131. return base::DirectoryExists(path) || base::CreateDirectory(path);
  132. }
  133. firmware_update::mojom::FirmwareUpdatePtr CreateUpdate(
  134. const FwupdUpdate& update_details,
  135. const std::string& device_id,
  136. const std::string& device_name) {
  137. auto update = firmware_update::mojom::FirmwareUpdate::New();
  138. update->device_id = device_id;
  139. update->device_name = base::UTF8ToUTF16(device_name);
  140. update->device_version = update_details.version;
  141. update->device_description = base::UTF8ToUTF16(update_details.description);
  142. update->priority =
  143. firmware_update::mojom::UpdatePriority(update_details.priority);
  144. update->filepath = update_details.filepath;
  145. update->checksum = update_details.checksum;
  146. return update;
  147. }
  148. constexpr net::NetworkTrafficAnnotationTag kFwupdFirmwareUpdateNetworkTag =
  149. net::DefineNetworkTrafficAnnotation("fwupd_firmware_update", R"(
  150. semantics {
  151. sender: "FWUPD firmware update"
  152. description:
  153. "Get the firmware update patch file from url and store it in the "
  154. "the device cache. This is used to update a specific peripheral's "
  155. "firmware."
  156. trigger:
  157. "Triggered by the user when they explicitly use the Firmware Update"
  158. " UI to update their peripheral."
  159. data: "None."
  160. destination: GOOGLE_OWNED_SERVICE
  161. }
  162. policy {
  163. cookies_allowed: NO
  164. setting:
  165. "This feature is used when the user updates their firmware."
  166. policy_exception_justification:
  167. "This request is made based on the user decision to update "
  168. "firmware."
  169. })");
  170. std::unique_ptr<network::SimpleURLLoader> CreateSimpleURLLoader(GURL url) {
  171. auto resource_request = std::make_unique<network::ResourceRequest>();
  172. resource_request->url = url;
  173. resource_request->method = "GET";
  174. resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  175. return network::SimpleURLLoader::Create(std::move(resource_request),
  176. kFwupdFirmwareUpdateNetworkTag);
  177. }
  178. int GetResponseCode(network::SimpleURLLoader* simple_loader) {
  179. if (simple_loader->ResponseInfo() && simple_loader->ResponseInfo()->headers)
  180. return simple_loader->ResponseInfo()->headers->response_code();
  181. else
  182. return -1;
  183. }
  184. // TODO(michaelcheco): Determine if more granular states are needed.
  185. firmware_update::mojom::UpdateState GetUpdateState(FwupdStatus fwupd_status) {
  186. switch (fwupd_status) {
  187. case FwupdStatus::kUnknown:
  188. return firmware_update::mojom::UpdateState::kUnknown;
  189. case FwupdStatus::kIdle:
  190. case FwupdStatus::kLoading:
  191. case FwupdStatus::kDecompressing:
  192. case FwupdStatus::kDeviceVerify:
  193. case FwupdStatus::kScheduling:
  194. case FwupdStatus::kDownloading:
  195. case FwupdStatus::kDeviceRead:
  196. case FwupdStatus::kDeviceErase:
  197. case FwupdStatus::kWaitingForAuth:
  198. case FwupdStatus::kDeviceBusy:
  199. case FwupdStatus::kShutdown:
  200. return firmware_update::mojom::UpdateState::kIdle;
  201. case FwupdStatus::kDeviceRestart:
  202. return firmware_update::mojom::UpdateState::kRestarting;
  203. case FwupdStatus::kDeviceWrite:
  204. return firmware_update::mojom::UpdateState::kUpdating;
  205. }
  206. }
  207. bool IsValidFirmwarePatchFile(const base::FilePath& filepath) {
  208. // Check if the extension is .cab.
  209. std::string extension = filepath.Extension();
  210. if (extension != kCabFileExtension) {
  211. return false;
  212. }
  213. // Check for invalid characters in filepath.
  214. return base::ContainsOnlyChars(filepath.BaseName().value(),
  215. kAllowedFilepathChars);
  216. }
  217. } // namespace
  218. FirmwareUpdateManager::FirmwareUpdateManager()
  219. : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  220. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  221. base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {
  222. DCHECK(FwupdClient::Get());
  223. FwupdClient::Get()->AddObserver(this);
  224. DCHECK_EQ(nullptr, g_instance);
  225. g_instance = this;
  226. }
  227. FirmwareUpdateManager::~FirmwareUpdateManager() {
  228. DCHECK_EQ(this, g_instance);
  229. FwupdClient::Get()->RemoveObserver(this);
  230. g_instance = nullptr;
  231. }
  232. // static
  233. FirmwareUpdateManager* FirmwareUpdateManager::Get() {
  234. DCHECK(g_instance);
  235. return g_instance;
  236. }
  237. // static
  238. bool FirmwareUpdateManager::IsInitialized() {
  239. return g_instance;
  240. }
  241. void FirmwareUpdateManager::AddObserver(Observer* observer) {
  242. observer_list_.AddObserver(observer);
  243. }
  244. void FirmwareUpdateManager::RemoveObserver(Observer* observer) {
  245. observer_list_.RemoveObserver(observer);
  246. }
  247. void FirmwareUpdateManager::NotifyCriticalFirmwareUpdateReceived() {
  248. for (auto& observer : observer_list_) {
  249. observer.OnFirmwareUpdateReceived();
  250. }
  251. }
  252. void FirmwareUpdateManager::RecordDeviceMetrics(int num_devices) {
  253. firmware_update::metrics::EmitDeviceCount(num_devices, is_first_response_);
  254. }
  255. void FirmwareUpdateManager::RecordUpdateMetrics() {
  256. firmware_update::metrics::EmitUpdateCount(
  257. updates_.size(), GetNumCriticalUpdates(), is_first_response_);
  258. }
  259. int FirmwareUpdateManager::GetNumCriticalUpdates() {
  260. int critical_update_count = 0;
  261. for (const auto& update : updates_) {
  262. if (update->priority == firmware_update::mojom::UpdatePriority::kCritical) {
  263. critical_update_count++;
  264. }
  265. }
  266. return critical_update_count;
  267. }
  268. void FirmwareUpdateManager::NotifyUpdateListObservers() {
  269. for (auto& observer : update_list_observers_) {
  270. observer->OnUpdateListChanged(mojo::Clone(updates_));
  271. }
  272. is_fetching_updates_ = false;
  273. }
  274. bool FirmwareUpdateManager::HasPendingUpdates() {
  275. return !devices_pending_update_.empty();
  276. }
  277. void FirmwareUpdateManager::ObservePeripheralUpdates(
  278. mojo::PendingRemote<firmware_update::mojom::UpdateObserver> observer) {
  279. update_list_observers_.Add(std::move(observer));
  280. if (!HasPendingUpdates()) {
  281. RequestAllUpdates();
  282. }
  283. }
  284. // TODO(michaelcheco): Handle the case where the app is closed during an
  285. // install.
  286. void FirmwareUpdateManager::ResetInstallState() {
  287. install_controller_receiver_.reset();
  288. update_progress_observer_.reset();
  289. }
  290. void FirmwareUpdateManager::PrepareForUpdate(
  291. const std::string& device_id,
  292. PrepareForUpdateCallback callback) {
  293. DCHECK(!device_id.empty());
  294. mojo::PendingRemote<firmware_update::mojom::InstallController>
  295. pending_remote = install_controller_receiver_.BindNewPipeAndPassRemote();
  296. install_controller_receiver_.set_disconnect_handler(base::BindOnce(
  297. &FirmwareUpdateManager::ResetInstallState, base::Unretained(this)));
  298. std::move(callback).Run(std::move(pending_remote));
  299. }
  300. void FirmwareUpdateManager::FetchInProgressUpdate(
  301. FetchInProgressUpdateCallback callback) {
  302. std::move(callback).Run(mojo::Clone(inflight_update_));
  303. }
  304. // Query all updates for all devices.
  305. void FirmwareUpdateManager::RequestAllUpdates() {
  306. if (should_show_notification_for_test_) {
  307. // Short circuit to immediately display notification.
  308. NotifyCriticalFirmwareUpdateReceived();
  309. return;
  310. }
  311. if (is_fetching_updates_) {
  312. return;
  313. }
  314. is_fetching_updates_ = true;
  315. RequestDevices();
  316. }
  317. void FirmwareUpdateManager::RequestDevices() {
  318. FwupdClient::Get()->RequestDevices();
  319. }
  320. void FirmwareUpdateManager::RequestUpdates(const std::string& device_id) {
  321. FwupdClient::Get()->RequestUpdates(device_id);
  322. }
  323. // TODO(jimmyxgong): Currently only looks for the local cache for the update
  324. // file. This needs to update to fetch the update file from a server and
  325. // download it to the local cache.
  326. void FirmwareUpdateManager::StartInstall(const std::string& device_id,
  327. const base::FilePath& filepath,
  328. base::OnceCallback<void()> callback) {
  329. base::FilePath root_dir;
  330. CHECK(base::PathService::Get(base::DIR_TEMP, &root_dir));
  331. const base::FilePath cache_path =
  332. root_dir.Append(FILE_PATH_LITERAL(kBaseRootPath))
  333. .Append(FILE_PATH_LITERAL(kCachePath));
  334. base::OnceClosure dir_created_callback =
  335. base::BindOnce(&FirmwareUpdateManager::CreateLocalPatchFile,
  336. weak_ptr_factory_.GetWeakPtr(), cache_path, device_id,
  337. filepath, std::move(callback));
  338. task_runner_->PostTaskAndReply(
  339. FROM_HERE,
  340. base::BindOnce(
  341. [](const base::FilePath& path) {
  342. if (!CreateDirIfNotExists(path)) {
  343. LOG(ERROR) << "Cannot create firmware update directory, "
  344. << "may be created already.";
  345. firmware_update::metrics::EmitInstallResult(
  346. firmware_update::metrics::FirmwareUpdateInstallResult::
  347. kFailedToCreateUpdateDirectory);
  348. }
  349. },
  350. cache_path),
  351. std::move(dir_created_callback));
  352. }
  353. void FirmwareUpdateManager::CreateLocalPatchFile(
  354. const base::FilePath& cache_path,
  355. const std::string& device_id,
  356. const base::FilePath& filepath,
  357. base::OnceCallback<void()> callback) {
  358. const base::FilePath patch_path =
  359. cache_path.Append(filepath.BaseName().value());
  360. // Create the patch file.
  361. task_runner_->PostTaskAndReplyWithResult(
  362. FROM_HERE,
  363. base::BindOnce(
  364. [](const base::FilePath& patch_path) {
  365. // TODO(michaelcheco): Verify that creating the empty file is
  366. // necessary.
  367. const bool write_file_success =
  368. base::WriteFile(patch_path, /*data=*/"");
  369. if (!write_file_success) {
  370. LOG(ERROR) << "Writing into the file: " << patch_path
  371. << " failed.";
  372. }
  373. return write_file_success;
  374. },
  375. patch_path),
  376. base::BindOnce(&FirmwareUpdateManager::MaybeDownloadFileToInternal,
  377. weak_ptr_factory_.GetWeakPtr(), patch_path, device_id,
  378. filepath, std::move(callback)));
  379. }
  380. void FirmwareUpdateManager::MaybeDownloadFileToInternal(
  381. const base::FilePath& patch_path,
  382. const std::string& device_id,
  383. const base::FilePath& filepath,
  384. base::OnceCallback<void()> callback,
  385. bool write_file_success) {
  386. if (!write_file_success) {
  387. std::move(callback).Run();
  388. return;
  389. }
  390. std::vector<base::FilePath::StringType> components = filepath.GetComponents();
  391. if (components[0] == kHttpsComponent) {
  392. // Firmware patch is available for download.
  393. DownloadFileToInternal(patch_path, device_id, filepath,
  394. std::move(callback));
  395. return;
  396. } else if (components[0] == kFileComponent) {
  397. // Firmware patch is already available from the local filesystem.
  398. size_t filepath_start = filepath.value().find(components[1]);
  399. if (filepath_start != std::string::npos) {
  400. const base::FilePath file(filepath.value().substr(filepath_start - 1));
  401. std::map<std::string, bool> options = {
  402. {"none", false}, {"force", true}, {"allow-reinstall", true}};
  403. task_runner_->PostTaskAndReplyWithResult(
  404. FROM_HERE, base::BindOnce(&OpenFileAndGetFileDescriptor, file),
  405. base::BindOnce(&FirmwareUpdateManager::OnGetFileDescriptor,
  406. weak_ptr_factory_.GetWeakPtr(), device_id,
  407. std::move(options), std::move(callback)));
  408. return;
  409. }
  410. }
  411. LOG(ERROR) << "Invalid file or download URI: " << filepath.value();
  412. std::move(callback).Run();
  413. }
  414. void FirmwareUpdateManager::DownloadFileToInternal(
  415. const base::FilePath& patch_path,
  416. const std::string& device_id,
  417. const base::FilePath& filepath,
  418. base::OnceCallback<void()> callback) {
  419. const std::string url = filepath.value();
  420. GURL download_url(fake_url_for_testing_.empty() ? url
  421. : fake_url_for_testing_);
  422. std::unique_ptr<network::SimpleURLLoader> simple_loader =
  423. CreateSimpleURLLoader(download_url);
  424. DCHECK(FwupdDownloadClient::Get());
  425. scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
  426. FwupdDownloadClient::Get()->GetURLLoaderFactory();
  427. // Save the pointer before moving `simple_loader` in the following call to
  428. // `DownloadToFile()`.
  429. auto* loader_ptr = simple_loader.get();
  430. loader_ptr->DownloadToFile(
  431. loader_factory.get(),
  432. base::BindOnce(&FirmwareUpdateManager::OnUrlDownloadedToFile,
  433. weak_ptr_factory_.GetWeakPtr(), device_id,
  434. std::move(simple_loader), std::move(callback)),
  435. patch_path);
  436. }
  437. void FirmwareUpdateManager::OnUrlDownloadedToFile(
  438. const std::string& device_id,
  439. std::unique_ptr<network::SimpleURLLoader> simple_loader,
  440. base::OnceCallback<void()> callback,
  441. base::FilePath download_path) {
  442. if (simple_loader->NetError() != net::OK) {
  443. LOG(ERROR) << "Downloading to file failed with error code: "
  444. << GetResponseCode(simple_loader.get()) << " with network error "
  445. << simple_loader->NetError();
  446. firmware_update::metrics::EmitInstallResult(
  447. firmware_update::metrics::FirmwareUpdateInstallResult::
  448. kFailedToDownloadToFile);
  449. std::move(callback).Run();
  450. return;
  451. }
  452. // TODO(jimmyxgong): Determine if this options map can be static or will need
  453. // to remain dynamic.
  454. // Fwupd Install Dbus flags, flag documentation can be found in
  455. // https://github.com/fwupd/fwupd/blob/main/libfwupd/fwupd-enums.h#L749.
  456. std::map<std::string, bool> options = {{"none", false},
  457. {"force", true},
  458. {"allow-older", true},
  459. {"allow-reinstall", true}};
  460. task_runner_->PostTaskAndReplyWithResult(
  461. FROM_HERE, base::BindOnce(&OpenFileAndGetFileDescriptor, download_path),
  462. base::BindOnce(&FirmwareUpdateManager::OnGetFileDescriptor,
  463. weak_ptr_factory_.GetWeakPtr(), device_id,
  464. std::move(options), std::move(callback)));
  465. }
  466. void FirmwareUpdateManager::OnGetFileDescriptor(
  467. const std::string& device_id,
  468. FirmwareInstallOptions options,
  469. base::OnceCallback<void()> callback,
  470. base::ScopedFD file_descriptor) {
  471. if (!file_descriptor.is_valid()) {
  472. LOG(ERROR) << "Invalid file descriptor.";
  473. firmware_update::metrics::EmitInstallResult(
  474. firmware_update::metrics::FirmwareUpdateInstallResult::
  475. kInvalidFileDescriptor);
  476. std::move(callback).Run();
  477. return;
  478. }
  479. DCHECK(inflight_update_.is_null());
  480. for (const auto& update : updates_) {
  481. if (update->device_id == device_id) {
  482. inflight_update_ = mojo::Clone(update);
  483. break;
  484. }
  485. }
  486. base::File patch_file(std::move(file_descriptor));
  487. task_runner_->PostTaskAndReplyWithResult(
  488. FROM_HERE,
  489. base::BindOnce(&VerifyChecksum, std::move(patch_file),
  490. inflight_update_->checksum),
  491. base::BindOnce(&FirmwareUpdateManager::InstallUpdate,
  492. weak_ptr_factory_.GetWeakPtr(), device_id,
  493. std::move(options), std::move(callback)));
  494. }
  495. void FirmwareUpdateManager::InstallUpdate(const std::string& device_id,
  496. FirmwareInstallOptions options,
  497. base::OnceCallback<void()> callback,
  498. base::File patch_file) {
  499. if (!patch_file.IsValid()) {
  500. inflight_update_.reset();
  501. std::move(callback).Run();
  502. return;
  503. }
  504. FwupdClient::Get()->InstallUpdate(
  505. device_id, base::ScopedFD(patch_file.TakePlatformFile()), options);
  506. std::move(callback).Run();
  507. }
  508. void FirmwareUpdateManager::OnDeviceListResponse(FwupdDeviceList* devices) {
  509. DCHECK(devices);
  510. DCHECK(!HasPendingUpdates());
  511. // Clear all cached updates prior to fetching the new update list.
  512. updates_.clear();
  513. RecordDeviceMetrics(devices->size());
  514. // Fire the observer with an empty list if there are no devices in the
  515. // response.
  516. if (devices->empty()) {
  517. NotifyUpdateListObservers();
  518. return;
  519. }
  520. for (const auto& device : *devices) {
  521. devices_pending_update_[device.id] = device;
  522. RequestUpdates(device.id);
  523. }
  524. }
  525. void FirmwareUpdateManager::ShowNotificationIfRequired() {
  526. for (const auto& update : updates_) {
  527. if (update->priority == firmware_update::mojom::UpdatePriority::kCritical &&
  528. !base::Contains(devices_already_notified_, update->device_id)) {
  529. devices_already_notified_.insert(update->device_id);
  530. NotifyCriticalFirmwareUpdateReceived();
  531. }
  532. }
  533. }
  534. void FirmwareUpdateManager::OnUpdateListResponse(const std::string& device_id,
  535. FwupdUpdateList* updates) {
  536. DCHECK(updates);
  537. DCHECK(base::Contains(devices_pending_update_, device_id));
  538. // If there are updates, then choose the first one.
  539. if (!updates->empty()) {
  540. auto device_name = devices_pending_update_[device_id].device_name;
  541. // Create a complete FirmwareUpdate and add to updates_.
  542. updates_.push_back(CreateUpdate(updates->front(), device_id, device_name));
  543. }
  544. // Remove the pending device.
  545. devices_pending_update_.erase(device_id);
  546. if (HasPendingUpdates()) {
  547. return;
  548. }
  549. RecordUpdateMetrics();
  550. // We only want to show the notification once, at startup.
  551. if (is_first_response_) {
  552. ShowNotificationIfRequired();
  553. }
  554. is_first_response_ = false;
  555. // Fire the observer since there are no remaining devices pending updates.
  556. NotifyUpdateListObservers();
  557. }
  558. void FirmwareUpdateManager::OnInstallResponse(bool success) {
  559. auto state = success ? firmware_update::mojom::UpdateState::kSuccess
  560. : firmware_update::mojom::UpdateState::kFailed;
  561. const auto result =
  562. success ? firmware_update::metrics::FirmwareUpdateInstallResult::kSuccess
  563. : firmware_update::metrics::FirmwareUpdateInstallResult::
  564. kInstallFailed;
  565. firmware_update::metrics::EmitInstallResult(result);
  566. // Success or Fail states are both considered 100% done.
  567. auto update = ash::firmware_update::mojom::InstallationProgress::New(
  568. /**percentage=*/100, state);
  569. // If the firmware update app is closed, the observer is no longer bound.
  570. if (update_progress_observer_.is_bound()) {
  571. update_progress_observer_->OnStatusChanged(std::move(update));
  572. }
  573. // Any updates are completed at this point, reset all cached.
  574. ResetInstallState();
  575. devices_already_notified_.erase(inflight_update_->device_id);
  576. inflight_update_.reset();
  577. // Request all updates to refresh the update list after an install.
  578. RequestAllUpdates();
  579. }
  580. void FirmwareUpdateManager::BindInterface(
  581. mojo::PendingReceiver<firmware_update::mojom::UpdateProvider>
  582. pending_receiver) {
  583. // Clear any bound receiver, since this service is a singleton and is bound
  584. // to the firmware updater UI it's possible that the app can be closed and
  585. // reopened multiple times resulting in multiple attempts to bind to this
  586. // receiver.
  587. receiver_.reset();
  588. receiver_.Bind(std::move(pending_receiver));
  589. }
  590. void FirmwareUpdateManager::OnPropertiesChangedResponse(
  591. FwupdProperties* properties) {
  592. if (!properties || !update_progress_observer_.is_bound()) {
  593. return;
  594. }
  595. const auto status = FwupdStatus(properties->status.value());
  596. const auto percentage = properties->percentage.value();
  597. VLOG(1) << "fwupd: OnPropertiesChangedResponse called with Status: "
  598. << GetFwupdStatusString(static_cast<FwupdStatus>(status))
  599. << " | Percentage: " << percentage;
  600. update_progress_observer_->OnStatusChanged(
  601. ash::firmware_update::mojom::InstallationProgress::New(
  602. percentage, GetUpdateState(status)));
  603. }
  604. void FirmwareUpdateManager::BeginUpdate(const std::string& device_id,
  605. const base::FilePath& filepath) {
  606. DCHECK(!filepath.empty());
  607. if (!IsValidFirmwarePatchFile(filepath)) {
  608. return;
  609. }
  610. StartInstall(device_id, filepath, /**callback=*/base::DoNothing());
  611. }
  612. void FirmwareUpdateManager::AddObserver(
  613. mojo::PendingRemote<firmware_update::mojom::UpdateProgressObserver>
  614. observer) {
  615. update_progress_observer_.reset();
  616. update_progress_observer_.Bind(std::move(observer));
  617. }
  618. } // namespace ash