chromium/chrome/browser/ash/extensions/file_manager/system_notification_manager.h

// 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 CHROME_BROWSER_ASH_EXTENSIONS_FILE_MANAGER_SYSTEM_NOTIFICATION_MANAGER_H_
#define CHROME_BROWSER_ASH_EXTENSIONS_FILE_MANAGER_SYSTEM_NOTIFICATION_MANAGER_H_

#include "ash/public/cpp/notification_utils.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_dialog.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "extensions/browser/event_router.h"
#include "storage/browser/file_system/file_system_url.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_delegate.h"

namespace file_manager {

namespace file_manager_private = extensions::api::file_manager_private;

class DriveFsEventRouter;

// Status of mounted removable devices.
enum SystemNotificationManagerMountStatus {
  // Initial state.
  MOUNT_STATUS_NO_RESULT,
  // No errors on the device.
  MOUNT_STATUS_SUCCESS,
  // Parent errors exist that may be overridden by child partitions.
  MOUNT_STATUS_ONLY_PARENT_ERROR,
  // A single child partition error.
  MOUNT_STATUS_CHILD_ERROR,
  // Multiple child partitions with at least one in error.
  MOUNT_STATUS_MULTIPART_ERROR,
};

// Enum of possible UMA values for histogram Notification.Show.
// Keep the order of this in sync with FileManagerNotificationType in
// tools/metrics/histograms/enums.xml.
enum class DeviceNotificationUmaType {
  DEVICE_NAVIGATION_ALLOW_APP_ACCESS = 0,
  DEVICE_NAVIGATION_APPS_HAVE_ACCESS = 1,
  DEVICE_NAVIGATION = 2,
  DEVICE_NAVIGATION_READONLY_POLICY = 3,
  DEVICE_IMPORT = 4,
  DEVICE_FAIL = 5,
  DEVICE_FAIL_UNKNOWN = 6,
  DEVICE_FAIL_UNKNOWN_READONLY = 7,
  DEVICE_EXTERNAL_STORAGE_DISABLED = 8,
  DEVICE_HARD_UNPLUGGED = 9,
  FORMAT_START = 10,
  FORMAT_SUCCESS = 11,
  FORMAT_FAIL = 12,
  RENAME_FAIL = 13,
  PARTITION_START = 14,
  PARTITION_SUCCESS = 15,
  PARTITION_FAIL = 16,
  kMaxValue = PARTITION_FAIL,
};

// Enum of possible UMA values for histogram Notification.UserAction.
// Keep the order of this in sync with FileManagerNotificationUserAction in
// tools/metrics/histograms/enums.xml.
enum class DeviceNotificationUserActionUmaType {
  OPEN_SETTINGS_FOR_ARC_STORAGE = 0,  // OPEN_EXTERNAL_STORAGE_PREFERENCES.
  OPEN_MEDIA_DEVICE_NAVIGATION = 1,
  OPEN_MEDIA_DEVICE_NAVIGATION_ARC = 2,
  OPEN_MEDIA_DEVICE_FAIL = 3,
  OPEN_MEDIA_DEVICE_IMPORT = 4,
  kMaxValue = OPEN_MEDIA_DEVICE_IMPORT,
};

// Histogram name for Notification.Show.
inline constexpr char kNotificationShowHistogramName[] =
    "FileBrowser.Notification.Show";

// Histogram name for Notification.UserAction.
inline constexpr char kNotificationUserActionHistogramName[] =
    "FileBrowser.Notification.UserAction";

// Generates a notification id based on `task_id`.
std::string GetNotificationId(io_task::IOTaskId task_id);

// Returns an instance of an 'ash' Notification with a bound click delegate.
// The notification will have Files app system notification theme.
std::unique_ptr<message_center::Notification> CreateSystemNotification(
    const std::string& notification_id,
    const std::u16string& title,
    const std::u16string& message,
    scoped_refptr<message_center::NotificationDelegate> delegate,
    message_center::RichNotificationData optional_fields =
        message_center::RichNotificationData());

// Returns an instance of an 'ash' Notification with title and message specified
// by string ID values (for 110n) with a bound click delegate.
// The notification will have Files app system notification theme.
std::unique_ptr<message_center::Notification> CreateSystemNotification(
    const std::string& notification_id,
    int title_id,
    int message_id,
    scoped_refptr<message_center::NotificationDelegate> delegate);

// Returns an instance of an 'ash' Notification with a bound click callback.
// The notification will have Files app system notification theme.
std::unique_ptr<message_center::Notification> CreateSystemNotification(
    const std::string& notification_id,
    const std::u16string& title,
    const std::u16string& message,
    const base::RepeatingClosure& click_callback);

// Manages creation/deletion and update of system notifications on behalf
// of the File Manager application.
class SystemNotificationManager {
 public:
  explicit SystemNotificationManager(Profile* profile);
  ~SystemNotificationManager();

  // Returns whether or not ANY SWA windows are opened. Does this by checking
  // the URL of all opened windows.
  bool DoFilesSwaWindowsExist();

  // Processes a device event to generate a system notification if needed.
  void HandleDeviceEvent(const file_manager_private::DeviceEvent& event);

  using NotificationPtr = std::unique_ptr<message_center::Notification>;

  // Returns an instance of an 'ash' Notification.
  NotificationPtr CreateNotification(const std::string& notification_id,
                                     const std::u16string& title,
                                     const std::u16string& message);

  // Returns an instance of an 'ash' Notification with progress value.
  NotificationPtr CreateProgressNotification(const std::string& notification_id,
                                             const std::u16string& title,
                                             const std::u16string& message,
                                             int progress);

  // Returns an instance of an 'ash' Notification with IOTask progress value.
  NotificationPtr CreateIOTaskProgressNotification(
      file_manager::io_task::IOTaskId task_id,
      const std::string& notification_id,
      const std::u16string& title,
      const std::u16string& message,
      const bool paused,
      int progress);

  // Click handler for the IOTask progress notification.
  void HandleIOTaskProgressNotificationClick(
      file_manager::io_task::IOTaskId task_id,
      const std::string& notification_id,
      const bool paused,
      std::optional<int> button_index);

  // Returns an instance of an 'ash' Notification with title and message
  // specified by string ID values (for 110n).
  NotificationPtr CreateNotification(const std::string& notification_id,
                                     int title_id,
                                     int message_id);

  using Event = extensions::Event;

  // Processes general extension events and can create a system notification.
  void HandleEvent(const Event& event);

  // Processes progress event from IOTaskController.
  void HandleIOTaskProgress(
      const file_manager::io_task::ProgressStatus& status);

  // Stores and updates the state of a device based on mount events for the top
  // level or any child partitions.
  SystemNotificationManagerMountStatus UpdateDeviceMountStatus(
      file_manager_private::MountCompletedEvent& event,
      const Volume& volume);

  // Processes volume mount completed events.
  void HandleMountCompletedEvent(
      file_manager_private::MountCompletedEvent& event,
      const Volume& volume);

  // Returns the message center display service that manages notifications.
  NotificationDisplayService* GetNotificationDisplayService();

  // Stores a reference to the DriveFS event router instance.
  void SetDriveFSEventRouter(DriveFsEventRouter* drivefs_event_router);

  // Stores a pointer to the IOTaskController instance to be able to cancel
  // tasks.
  void SetIOTaskController(
      file_manager::io_task::IOTaskController* io_task_controller);

 private:
  // Handles clicks on the DriveFS bulk-pinning error notification.
  void HandleBulkPinningNotificationClick();

  // Make notification for DriveFS bulk-pinning error.
  NotificationPtr MakeBulkPinningErrorNotification(const Event& event);

  // Make notifications for DriveFS sync errors.
  NotificationPtr MakeDriveSyncErrorNotification(const Event& event);

  // Click handler for the Drive offline confirmation dialog notification.
  void HandleDriveDialogClick(std::optional<int> button_index);

  // Make notification from the DriveFS offline settings event.
  NotificationPtr MakeDriveConfirmDialogNotification(const Event& event);

  // Click handler for the removable device notification.
  void HandleRemovableNotificationClick(
      const std::string& path,
      const std::vector<DeviceNotificationUserActionUmaType>&
          uma_types_for_buttons,
      std::optional<int> button_index);

  // Click handler for Data Leak Prevention or Enterprise Connectors policy
  // notifications.
  void HandleDataProtectionPolicyNotificationClick(
      base::RepeatingClosure proceed_callback,
      base::RepeatingClosure cancel_callback,
      std::optional<int> button_index);

  // Click handler for the progress notification.
  void HandleProgressClick(const std::string& notification_id,
                           std::optional<int> button_index);

  // Makes a notification instance for mount errors.
  NotificationPtr MakeMountErrorNotification(
      file_manager_private::MountCompletedEvent& event,
      const Volume& volume);

  // Makes a notification instance for removable devices.
  NotificationPtr MakeRemovableNotification(
      file_manager_private::MountCompletedEvent& event,
      const Volume& volume);

  // Makes a notification instance for Data Protection progress notifications.
  NotificationPtr MakeDataProtectionPolicyProgressNotification(
      const std::string& notification_id,
      const file_manager::io_task::ProgressStatus& status);

  // Helper function to show a data protection policy dialog.
  void ShowDataProtectionPolicyDialog(file_manager::io_task::IOTaskId task_id,
                                      policy::FilesDialogType type);

  // Helper function bound to notification instances that hides notifications.
  void Dismiss(const std::string& notification_id);

  // Helper function to cancel a task.
  void CancelTask(file_manager::io_task::IOTaskId task_id);

  // Helper function to resume a task.
  void ResumeTask(file_manager::io_task::IOTaskId task_id,
                  policy::Policy policy);

  // Maps device paths to their mount status.
  // This is used for removable devices with single/multiple partitions.
  // e.g. the same device path could have 2 partitions that each generate a
  // mount event. One partition could have a known file system and the other an
  //      unknown file system. Different combinations of known/unknown file
  //      systems on a multi-partition devices require this map to generate
  //      the correct system notification when errors occur.
  std::map<std::string, SystemNotificationManagerMountStatus> mount_status_;

  // User profile.
  const raw_ptr<Profile, DanglingUntriaged> profile_;

  // Application name (used for notification display source).
  std::u16string const app_name_;

  // DriveFS event router: not owned.
  raw_ptr<DriveFsEventRouter, DanglingUntriaged> drivefs_event_router_ =
      nullptr;

  // IOTaskController is owned by VolumeManager.
  raw_ptr<file_manager::io_task::IOTaskController, DanglingUntriaged>
      io_task_controller_ = nullptr;

  // Keep track of the bulk-pinning stage.
  using BulkPinStage = file_manager_private::BulkPinStage;
  BulkPinStage bulk_pin_stage_ = BulkPinStage::kNone;

  // base::WeakPtr{this} factory.
  base::WeakPtrFactory<SystemNotificationManager> weak_ptr_factory_{this};
};

}  // namespace file_manager

#endif  // CHROME_BROWSER_ASH_EXTENSIONS_FILE_MANAGER_SYSTEM_NOTIFICATION_MANAGER_H_