| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_ |
| #define CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/files/file.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/common/extensions/api/image_writer_private.h" |
| #include "crypto/obsolete/md5.h" |
| #include "extensions/common/extension_id.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "chromeos/ash/components/disks/disk_mount_manager.h" |
| #endif |
| |
| namespace image_writer_api = extensions::api::image_writer_private; |
| |
| namespace base { |
| class FilePath; |
| } // namespace base |
| |
| namespace extensions { |
| namespace image_writer { |
| |
| inline constexpr int kProgressComplete = 100; |
| |
| class OperationManager; |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| class ImageWriterUtilityClient; |
| #endif |
| |
| // Encapsulates an operation being run on behalf of the |
| // OperationManager. Construction of the operation does not start |
| // anything. The operation's Start method should be called to start it, and |
| // then the Cancel method will stop it. The operation will call back to the |
| // OperationManager periodically or on any significant event. |
| // |
| // Each stage of the operation is generally divided into multiple phases with |
| // Start() being the first phase and Complete() being the last. All phases |
| // except Complete() run on blocking thread and Complete() runs on the UI |
| // thread. |
| // |
| // TODO(haven): This class is current refcounted because it is owned by the |
| // OperationManager on the UI thread but needs to do work on blocking threads. |
| // There is probably a better way to organize this so that it can be represented |
| // by a WeakPtr, but those are not thread-safe. Additionally, if destruction is |
| // done on the UI thread then that causes problems if any of the fields were |
| // allocated/accessed on the blocking thread. http://crbug.com/344713 |
| class Operation : public base::RefCountedThreadSafe<Operation> { |
| public: |
| using StartWriteCallback = base::OnceCallback<void(bool, const std::string&)>; |
| using CancelWriteCallback = |
| base::OnceCallback<void(bool, const std::string&)>; |
| |
| Operation(base::WeakPtr<OperationManager> manager, |
| const ExtensionId& extension_id, |
| const std::string& device_path, |
| const base::FilePath& download_folder); |
| |
| Operation(const Operation&) = delete; |
| Operation& operator=(const Operation&) = delete; |
| |
| // Starts the operation. |
| void Start(); |
| |
| // Cancel the operation. This must be called to clean up internal state and |
| // cause the the operation to actually stop. It will not be destroyed until |
| // all callbacks have completed. |
| void Cancel(); |
| |
| // Aborts the operation, cancelling it and generating an error. |
| void Abort(); |
| |
| // Informational getters. |
| int GetProgress(); |
| image_writer_api::Stage GetStage(); |
| |
| // Posts `task` to Operation's `task_runner_`. |
| void PostTask(base::OnceClosure task); |
| |
| protected: |
| virtual ~Operation(); |
| |
| scoped_refptr<base::SequencedTaskRunner> task_runner() { |
| return task_runner_; |
| } |
| |
| // This function should be overriden by subclasses to set up the work of the |
| // operation. It will be called from Start(). |
| virtual void StartImpl() = 0; |
| |
| // Extracts the current file if it's an archive. The current_file will be set |
| // to the extracted file. |
| void Extract(base::OnceClosure continuation); |
| |
| // Writes the current file to device_path. |
| void Write(base::OnceClosure continuation); |
| |
| // Verifies that the current file and device_path contents match. |
| void VerifyWrite(base::OnceClosure continuation); |
| |
| // Completes the operation. |
| void Finish(); |
| |
| // Generates an error. |
| // `error_message` is used to create an OnWriteError event which is |
| // sent to the extension |
| void Error(const std::string& error_message); |
| |
| // Set `progress_` and send an event. Progress should be in the interval |
| // [0,100] |
| void SetProgress(int progress); |
| // Change to a new `stage_` and set `progress_` to zero. Triggers a progress |
| // event. |
| void SetStage(image_writer_api::Stage stage); |
| |
| // Can be queried to safely determine if the operation has been cancelled. |
| bool IsCancelled(); |
| |
| // Adds a callback that will be called during clean-up, whether the operation |
| // is aborted, encounters and error, or finishes successfully. These |
| // functions will be run on `task_runner_`. |
| void AddCleanUpFunction(base::OnceClosure callback); |
| |
| // Completes the current operation (progress set to 100) and runs the |
| // continuation. |
| void CompleteAndContinue(base::OnceClosure continuation); |
| |
| // If `file_size` is non-zero, only `file_size` bytes will be read from file, |
| // otherwise the entire file will be read. |
| // `progress_scale` is a percentage to which the progress will be scale, e.g. |
| // a scale of 50 means it will increment from 0 to 50 over the course of the |
| // sum. `progress_offset` is an percentage that will be added to the progress |
| // of the MD5 sum before updating `progress_` but after scaling. |
| void GetMD5SumOfFile(const base::FilePath& file, |
| base::OnceCallback<void(const std::string&)> callback); |
| |
| bool IsRunningInCorrectSequence() const; |
| |
| base::WeakPtr<OperationManager> manager_; |
| const ExtensionId extension_id_; |
| |
| base::FilePath image_path_; |
| base::FilePath device_path_; |
| |
| // Temporary directory to store files as we go. |
| std::unique_ptr<base::ScopedTempDir> temp_dir_; |
| |
| private: |
| friend class base::RefCountedThreadSafe<Operation>; |
| friend class OperationForTest; |
| friend class ImageWriterUtilityClientTest; |
| friend class WriteFromUrlOperationForTest; |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Ensures the client is started. This may be called many times but will only |
| // instantiate one client which should exist for the lifetime of the |
| // Operation. |
| void StartUtilityClient(); |
| |
| // Stops the client. This must be called to ensure the utility process can |
| // shutdown. |
| void StopUtilityClient(); |
| |
| // Reports progress from the client, transforming from bytes to percentage. |
| virtual void WriteImageProgress(int64_t total_bytes, int64_t curr_bytes); |
| |
| scoped_refptr<ImageWriterUtilityClient> image_writer_client_; |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Unmounts all volumes on `device_path_`. |
| void UnmountVolumes(base::OnceClosure continuation); |
| // Starts the write after unmounting. |
| void UnmountVolumesCallback(base::OnceClosure continuation, |
| ash::MountError error_code); |
| // Starts the ImageBurner write. Note that target_path is the file path of |
| // the device where device_path has been a system device path. |
| void StartWriteOnUIThread(const std::string& target_path, |
| base::OnceClosure continuation); |
| void OnBurnFinished(base::OnceClosure continuation, |
| const std::string& target_path, |
| bool success, |
| const std::string& error); |
| void OnBurnProgress(const std::string& target_path, |
| int64_t num_bytes_burnt, |
| int64_t total_size); |
| void OnBurnError(); |
| #endif |
| |
| // Incrementally calculates the MD5 sum of a file. |
| void MD5Chunk(base::File file, |
| crypto::obsolete::Md5 md5, |
| size_t bytes_processed, |
| size_t bytes_total, |
| const base::OnceCallback<void(const std::string&)> callback); |
| |
| // Callbacks for Extractor. |
| void OnExtractOpenComplete(const base::FilePath& image_path); |
| void OnExtractProgress(int64_t total_bytes, int64_t progress_bytes); |
| void OnExtractFailure(const std::string& error); |
| |
| // Runs all cleanup functions. |
| void CleanUp(); |
| |
| // `stage_` and `progress_` are owned by the FILE thread, use `SetStage` and |
| // `SetProgress` to update. Progress should be in the interval [0,100] |
| image_writer_api::Stage stage_; |
| int progress_; |
| |
| // Cleanup operations that must be run. All these functions are run on |
| // `task_runner_`. |
| std::vector<base::OnceClosure> cleanup_functions_; |
| |
| static constexpr base::TaskTraits blocking_task_traits() { |
| return { |
| // Requires I/O. |
| base::MayBlock(), |
| // Apps (e.g. Chromebook Recovery Utility) present UI feedback based on |
| // an operation, but it's not on critical path. |
| base::TaskPriority::USER_VISIBLE, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, |
| }; |
| } |
| |
| // The download folder on Chrome OS. |
| const base::FilePath download_folder_; |
| |
| // Sequenced task runner where all I/O operation will be performed. |
| // Most of the methods of this class run in this task runner. |
| scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| }; |
| |
| } // namespace image_writer |
| } // namespace extensions |
| |
| #endif // CHROME_BROWSER_EXTENSIONS_API_IMAGE_WRITER_PRIVATE_OPERATION_H_ |