chromium/ash/system/privacy_hub/camera_privacy_switch_controller.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SYSTEM_PRIVACY_HUB_CAMERA_PRIVACY_SWITCH_CONTROLLER_H_
#define ASH_SYSTEM_PRIVACY_HUB_CAMERA_PRIVACY_SWITCH_CONTROLLER_H_

#include <memory>
#include <string>

#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "components/prefs/pref_change_registrar.h"
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"

namespace ash {

class PrivacyHubDelegate;

// Enumeration of camera switch states.
enum class CameraSWPrivacySwitchSetting { kDisabled, kEnabled };

// Abstraction for communication with the backend camera switch.
class CameraPrivacySwitchAPI {
 public:
  virtual ~CameraPrivacySwitchAPI() = default;

  // Sets the SW Privacy Switch value in the CrOS Camera service.
  virtual void SetCameraSWPrivacySwitch(CameraSWPrivacySwitchSetting) = 0;
};

// A singleton class that acts as a bridge between Privacy Hub UI and backend.
// It listens on both ends and changes UI to reflect changes in
// the backend and notifies the backend of changes in the user
// preference setting.
class ASH_EXPORT CameraPrivacySwitchController
    : public SessionObserver,
      public media::CameraPrivacySwitchObserver,
      public base::SupportsUserData {
 public:
  CameraPrivacySwitchController();
  CameraPrivacySwitchController(const CameraPrivacySwitchController&) = delete;
  CameraPrivacySwitchController& operator=(
      const CameraPrivacySwitchController&) = delete;
  ~CameraPrivacySwitchController() override;

  // SessionObserver:
  void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;

  // media::CameraPrivacySwitchObserver:
  void OnCameraSWPrivacySwitchStateChanged(
      cros::mojom::CameraPrivacySwitchState state) override;

  // Handles changes in the user pref ( e.g. toggling the camera switch on
  // Privacy Hub UI).
  void OnPreferenceChanged(const std::string& pref_name);

  // Retrieves the current value of the user pref.
  CameraSWPrivacySwitchSetting GetUserSwitchPreference() const;

  // Sets Privacy switch API for testing.
  void SetCameraPrivacySwitchAPIForTest(
      std::unique_ptr<CameraPrivacySwitchAPI> switch_api);

  // Sets/unsets the UI frontend delegate.
  void SetFrontend(PrivacyHubDelegate* frontend);

  // Disable camera access, and prevent the user from re-enabling it by graying
  // out the switch in the UI.
  // Used to prevent a remote Chrome Remote Desktop admin from turning on the
  // camera to spy on a local user.
  void SetForceDisableCameraAccess(bool value);
  bool IsCameraAccessForceDisabled() const;

  // Gets the instance from Shell.
  static CameraPrivacySwitchController* Get();

  // Handles the change in the number of cameras.
  void OnCameraCountChanged(int new_camera_count);

  // This is called when the set of applications accessing the camera changes.
  // `application_added` being true means a new applications has started
  // accessing the camera. `application_added` being false means one of the
  // active applications has stopped accessing the camera.
  void ActiveApplicationsChanged(bool application_added);

  // Checks if we use the fallback solution for the camera LED.
  // (go/privacy-hub:camera-led-fallback).
  // TODO(b/289510726): remove when all cameras fully support the software
  // switch.
  bool UsingCameraLEDFallback();

  // Returns false if the camera is globally blocked by the OS level switch.
  bool IsCameraUsageAllowed() const;

 private:
  // Sets the value of the global camera permission in the camera backend.
  void SetCameraSWPrivacySwitch(CameraSWPrivacySwitchSetting value);

  // Sets the value of the user pref in the pref service.
  void SetUserSwitchPreference(CameraSWPrivacySwitchSetting value);

  PrefService& prefs();
  const PrefService& prefs() const;
  void RestorePreviousPrefValueMaybe();
  void StorePreviousPrefValue();

  void ShowNotification() VALID_CONTEXT_REQUIRED(sequence_checker_);
  void RemoveNotification() VALID_CONTEXT_REQUIRED(sequence_checker_);
  void UpdateNotification() VALID_CONTEXT_REQUIRED(sequence_checker_);
  void ScheduleNotificationRemoval() VALID_CONTEXT_REQUIRED(sequence_checker_);
  bool InNotificationExtensionPeriod()
      VALID_CONTEXT_REQUIRED(sequence_checker_);

  raw_ptr<PrivacyHubDelegate> frontend_ = nullptr;
  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
  std::unique_ptr<CameraPrivacySwitchAPI> switch_api_;
  bool is_camera_observer_added_ = false;
  bool force_disable_camera_access_ = false;

  SEQUENCE_CHECKER(sequence_checker_);

  int GUARDED_BY_CONTEXT(sequence_checker_)
      active_applications_using_camera_count_ = 0;
  int camera_count_ = -1;
  base::Time GUARDED_BY_CONTEXT(sequence_checker_)
      last_active_notification_update_time_;

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

}  // namespace ash

#endif  // ASH_SYSTEM_PRIVACY_HUB_CAMERA_PRIVACY_SWITCH_CONTROLLER_H_