chromium/chrome/browser/ash/policy/skyvault/drive_skyvault_uploader.h

// Copyright 2024 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_ASH_POLICY_SKYVAULT_DRIVE_SKYVAULT_UPLOADER_H_
#define CHROME_BROWSER_ASH_POLICY_SKYVAULT_DRIVE_SKYVAULT_UPLOADER_H_

#include <optional>
#include <string>

#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/ash/policy/skyvault/policy_utils.h"
#include "chromeos/ash/components/drivefs/drivefs_host.h"
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"

class Profile;

namespace policy::local_user_files {

// Manages the "upload to Drive" workflow as part of the SkyVault files
// migration. Starts with moving the file to the cloud. Gets upload status by
// observing move and Drive events. Calls the UploadCallback with once the
// upload is completed, passing the error if any occurred.
// TODO(b/353475473): Extract code shared with DriveUploadObserver, if possible.
class DriveSkyvaultUploader
    : public file_manager::io_task::IOTaskController::Observer,
      drivefs::DriveFsHost::Observer,
      drive::DriveIntegrationService::Observer {
 public:
  using UploadCallback =
      base::OnceCallback<void(std::optional<MigrationUploadError> error)>;

  DriveSkyvaultUploader(Profile* profile,
                        const base::FilePath& file_path,
                        const base::FilePath& target_path,
                        UploadCallback callback);
  ~DriveSkyvaultUploader() override;

  // Starts the upload workflow:
  // - Copy the file via an IO task.
  // - Sync to Drive.
  // - Remove the source file in case of a move operation. Move mode of the
  //   `CopyOrMoveIOTask` is not used because the source file should only be
  //   deleted at the end of the sync operation.
  // Initiated by the `Upload` static method.
  void Run();

  DriveSkyvaultUploader(const DriveSkyvaultUploader&) = delete;
  DriveSkyvaultUploader& operator=(const DriveSkyvaultUploader&) = delete;

  void SetFailDeleteForTesting(bool fail);

 private:
  // Starts the IOTask to upload the file to Google Drive to
  // `destination_folder_path`, if it was created successfully and fails the
  // operation otherwise.
  void CreateCopyIOTask(const base::FilePath& destination_folder_path,
                        bool created);

  // Called when copy to Drive completes. Cleans up files if needed, or
  // completes the operation immediately. Saves `error` so it's not overridden
  // if delete fails.
  void OnEndCopy(std::optional<MigrationUploadError> error = std::nullopt);

  // Called after unrecoverable error or when all tasks complete successfully.
  // Invokes the upload callback, passing the error if any occurred.
  void OnEndUpload();

  // Callback for when ImmediatelyUpload() is called on DriveFS.
  void ImmediatelyUploadDone(drive::FileError error);

  // Directs IO task status updates to |OnCopyStatus| or |OnDeleteStatus| based
  // on task id.
  void OnIOTaskStatus(
      const file_manager::io_task::ProgressStatus& status) override;

  // Observes copy to Drive IO task status updates. Calls `OnEndCopy` upon any
  // error.
  void OnCopyStatus(const file_manager::io_task::ProgressStatus& status);

  // Observes delete IO task status updates from the delete task for cleaning up
  // the source file. Calls `OnEndUpload` once the delete is finished.
  void OnDeleteStatus(const file_manager::io_task::ProgressStatus& status);

  // DriveFsHost::Observer implementation.
  void OnUnmounted() override;
  void OnSyncingStatusUpdate(
      const drivefs::mojom::SyncingStatus& status) override;
  void OnError(const drivefs::mojom::DriveError& error) override;

  // DriveIntegrationService::Observer implementation.
  void OnDriveConnectionStatusChanged(
      drive::util::ConnectionStatus status) override;

  // Test-only: Simulates a delete failure if true. Actual result of the
  // DeleteIO task is ignored.
  bool fail_delete_for_testing_ = false;

  const raw_ptr<Profile> profile_;
  scoped_refptr<storage::FileSystemContext> file_system_context_;
  const raw_ptr<drive::DriveIntegrationService> drive_integration_service_;

  // Upload details:
  const storage::FileSystemURL source_url_;  // Source file URL
  const base::FilePath target_path_;         // Target folder path on Drive
  UploadCallback callback_;                  // Invoked on completion

  // Tracks upload progress:
  std::optional<file_manager::io_task::IOTaskId> observed_copy_task_id_ =
      std::nullopt;
  std::optional<file_manager::io_task::IOTaskId> observed_delete_task_id_ =
      std::nullopt;
  base::FilePath observed_absolute_dest_path_;
  base::FilePath observed_relative_drive_path_;

  // Whether `EndCopy()` was called.
  bool copy_ended_ = false;

  // Stores the first encountered error, if any.
  std::optional<MigrationUploadError> error_;

  raw_ptr<file_manager::io_task::IOTaskController> io_task_controller_ =
      nullptr;
  base::ScopedObservation<file_manager::io_task::IOTaskController,
                          file_manager::io_task::IOTaskController::Observer>
      io_task_controller_observer_{this};

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

}  // namespace policy::local_user_files

#endif  // CHROME_BROWSER_ASH_POLICY_SKYVAULT_DRIVE_SKYVAULT_UPLOADER_H_