chromium/chrome/browser/ash/scanning/scan_service.h

// Copyright 2020 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_SCANNING_SCAN_SERVICE_H_
#define CHROME_BROWSER_ASH_SCANNING_SCAN_SERVICE_H_

#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "ash/webui/scanning/mojom/scanning.mojom.h"
#include "base/cancelable_callback.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ash/scanning/scanning_file_path_helper.h"
#include "chromeos/ash/components/dbus/lorgnette/lorgnette_service.pb.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/wake_lock/power_save_blocker/power_save_blocker.h"

namespace base {
class SequencedTaskRunner;
}  // namespace base

namespace content {
class BrowserContext;
}  // namespace content

namespace ash {

class LorgnetteScannerManager;

// Implementation of the ash::scanning::mojom::ScanService interface. Used
// by the scanning WebUI (chrome://scanning) to get connected scanners, obtain
// scanner capabilities, and perform scans.
class ScanService : public scanning::mojom::ScanService,
                    public scanning::mojom::MultiPageScanController,
                    public KeyedService {
 public:
  ScanService(LorgnetteScannerManager* lorgnette_scanner_manager,
              base::FilePath my_files_path,
              base::FilePath google_drive_path,
              content::BrowserContext* context);
  ~ScanService() override;

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

  // scanning::mojom::ScanService:
  void GetScanners(GetScannersCallback callback) override;
  void GetScannerCapabilities(const base::UnguessableToken& scanner_id,
                              GetScannerCapabilitiesCallback callback) override;
  void StartScan(const base::UnguessableToken& scanner_id,
                 scanning::mojom::ScanSettingsPtr settings,
                 mojo::PendingRemote<scanning::mojom::ScanJobObserver> observer,
                 StartScanCallback callback) override;
  void StartMultiPageScan(
      const base::UnguessableToken& scanner_id,
      scanning::mojom::ScanSettingsPtr settings,
      mojo::PendingRemote<scanning::mojom::ScanJobObserver> observer,
      StartMultiPageScanCallback callback) override;
  void CancelScan() override;

  // scanning::mojom::MultiPageScanController:
  void ScanNextPage(const base::UnguessableToken& scanner_id,
                    scanning::mojom::ScanSettingsPtr settings,
                    ScanNextPageCallback callback) override;
  void RemovePage(uint32_t page_index) override;
  void RescanPage(const base::UnguessableToken& scanner_id,
                  scanning::mojom::ScanSettingsPtr settings,
                  uint32_t page_index,
                  ScanNextPageCallback callback) override;
  void CompleteMultiPageScan() override;

  // Binds receiver_ by consuming |pending_receiver|.
  void BindInterface(
      mojo::PendingReceiver<scanning::mojom::ScanService> pending_receiver);

  // Returns |scanned_images_| to verify the correct images are added/removed in
  // unit tests.
  std::vector<std::string> GetScannedImagesForTesting() const;

 private:
  // KeyedService:
  void Shutdown() override;

  // Processes the result of calling LorgnetteScannerManager::GetScannerNames().
  void OnScannerNamesReceived(GetScannersCallback callback,
                              std::vector<std::string> scanner_names);

  // Processes the result of calling
  // LorgnetteScannerManager::GetScannerCapabilities().
  void OnScannerCapabilitiesReceived(
      GetScannerCapabilitiesCallback callback,
      const std::optional<lorgnette::ScannerCapabilities>& capabilities);

  // Receives progress updates after calling LorgnetteScannerManager::Scan().
  // |page_number| indicates the page the |progress_percent| corresponds to.
  void OnProgressPercentReceived(uint32_t progress_percent,
                                 uint32_t page_number);

  // Processes each |scanned_image| received after calling
  // LorgnetteScannerManager::Scan(). |scan_to_path| is where images will be
  // saved, and |file_type| specifies the file type to use when saving scanned
  // images. If |page_index_to_replace| exists then |scanned_image| will replace
  // an existing scanned image instead of being appended.
  void OnPageReceived(const base::FilePath& scan_to_path,
                      const scanning::mojom::FileType file_type,
                      const std::optional<uint32_t> page_index_to_replace,
                      std::string scanned_image,
                      uint32_t page_number);

  // Processes the final result of calling LorgnetteScannerManager::Scan().
  // |failure_mode| is set to SCAN_FAILURE_MODE_NO_FAILURE when the scan
  // succeeds; otherwise, its value indicates what caused the scan to fail.
  void OnScanCompleted(bool is_multi_page_scan,
                       lorgnette::ScanFailureMode failure_mode);

  // For a multi-page scan, when a page scan completes, report a failure if it
  // exists.
  void OnMultiPageScanPageCompleted(lorgnette::ScanFailureMode failure_mode);

  // Processes the final result of calling
  // LorgnetteScannerManager::CancelScan().
  void OnCancelCompleted(bool success);

  // Called once the task runner finishes saving a PDF file.
  void OnPdfSaved(const bool success);

  // Called once the task runner finishes saving a page of a scan.
  void OnPageSaved(const base::FilePath& saved_file_path);

  // Sends the scan request to the scanner.
  bool SendScanRequest(
      const base::UnguessableToken& scanner_id,
      scanning::mojom::ScanSettingsPtr settings,
      const std::optional<uint32_t> page_index_to_replace,
      base::OnceCallback<void(lorgnette::ScanFailureMode failure_mode)>
          completion_callback);

  // Called once the task runner finishes saving the last page of a scan.
  void OnAllPagesSaved(lorgnette::ScanFailureMode failure_mode);

  // Sets the local member variables back to their initial empty state.
  void ClearScanState();

  // Sets the ScanJobOberver for a new scan.
  void SetScanJobObserver(
      mojo::PendingRemote<scanning::mojom::ScanJobObserver> observer);

  // Resets the mojo::Receiver |multi_page_controller_receiver_|.
  void ResetMultiPageScanController();

  // Determines whether the service supports saving scanned images to
  // |file_path|.
  bool FilePathSupported(const base::FilePath& file_path);

  // Returns the scanner name corresponding to the given |scanner_id| or an
  // empty string if the name cannot be found.
  std::string GetScannerName(const base::UnguessableToken& scanner_id);

  // Map of scanner IDs to display names. Used to pass the correct display name
  // to LorgnetteScannerManager when clients provide an ID.
  base::flat_map<base::UnguessableToken, std::string> scanner_names_;

  // Receives and dispatches method calls to this implementation of the
  // ash::scanning::mojom::ScanService interface.
  mojo::Receiver<scanning::mojom::ScanService> receiver_{this};

  // Receives and dispatches method calls to this implementation of the
  // ash::scanning::mojom::MultiPageScanController interface.
  mojo::Receiver<scanning::mojom::MultiPageScanController>
      multi_page_controller_receiver_{this};

  // Used to send scan job events to an observer. The remote is bound when a
  // scan job is started and is disconnected when the scan job is complete.
  mojo::Remote<scanning::mojom::ScanJobObserver> scan_job_observer_;

  // Unowned. Used to get scanner information and perform scans.
  raw_ptr<LorgnetteScannerManager> lorgnette_scanner_manager_;

  // The browser context from which scans are initiated.
  const raw_ptr<content::BrowserContext> context_;

  // Indicates whether there was a failure to save scanned images.
  bool page_save_failed_;

  // The scanned images used to create a multipage PDF.
  std::vector<std::string> scanned_images_;

  // The time a scan was started. Used in filenames when saving scanned images.
  base::Time start_time_;

  // The file paths of the pages scanned in a scan job.
  std::vector<base::FilePath> scanned_file_paths_;

  // Task runner used to convert and save scanned images.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  // Tracks the number of pages scanned for histogram recording.
  int num_pages_scanned_;

  // Indicates whether alternate pages must be rotated to account for an ADF
  // scanner that flips them.
  bool rotate_alternate_pages_;

  // Stores the dots per inch (DPI) of the requested scan.
  std::optional<int> scan_dpi_;

  // The time at which GetScanners() is called. Used to record the time between
  // a user launching the Scan app and being able to interact with it.
  base::TimeTicks get_scanners_time_;

  // The time a multi-page scan session starts. Used to record the duration of a
  // multi-page scan session.
  base::TimeTicks multi_page_start_time_;

  // Helper class for for file path manipulation and verification.
  ScanningFilePathHelper file_path_helper_;

  // Wake lock to ensure system does not suspend during a scan job.
  std::unique_ptr<device::PowerSaveBlocker> wake_lock_;

  // Called if there is no response from the scanner after a timeout. Used to
  // ensure the wake lock will be released if there is an error from the
  // scanner or backend.
  base::CancelableOnceCallback<void(ScanService*, bool,
                               lorgnette::ScanFailureMode)> timeout_callback_;

  // Needs to be last member variable.
  base::WeakPtrFactory<ScanService> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_SCANNING_SCAN_SERVICE_H_