blob: a715043e3de64af5480e786794b9dbcdea9199b6 [file] [log] [blame]
// 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.
#include "ash/system/privacy_hub/camera_privacy_switch_controller.h"
#include <utility>
#include <vector>
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/privacy_hub/privacy_hub_controller.h"
#include "ash/system/privacy_hub/privacy_hub_metrics.h"
#include "ash/test/ash_test_base.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/public/cpp/notification.h"
using testing::_;
namespace ash {
namespace {
class MockSwitchAPI : public CameraPrivacySwitchAPI {
public:
MOCK_METHOD(void,
SetCameraSWPrivacySwitch,
(CameraSWPrivacySwitchSetting),
(override));
};
class MockFrontendAPI : public PrivacyHubDelegate {
public:
MOCK_METHOD(void,
CameraHardwareToggleChanged,
(cros::mojom::CameraPrivacySwitchState state),
(override));
void AvailabilityOfMicrophoneChanged(bool) override {}
void MicrophoneHardwareToggleChanged(bool) override {}
};
class FakeSensorDisabledNotificationDelegate
: public SensorDisabledNotificationDelegate {
public:
std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override {
if (sensor == Sensor::kCamera) {
return apps_accessing_camera_;
}
return {};
}
void LaunchAppAccessingCamera(const std::u16string& app_name) {
apps_accessing_camera_.insert(apps_accessing_camera_.begin(), app_name);
}
private:
std::vector<std::u16string> apps_accessing_camera_;
};
message_center::Notification* FindNotificationById(const std::string& id) {
return message_center::MessageCenter::Get()->FindNotificationById(id);
}
class RemoveNotificationWaiter : public message_center::MessageCenterObserver {
public:
explicit RemoveNotificationWaiter(const std::string& notification_id)
: notification_id_(notification_id) {
message_center::MessageCenter::Get()->AddObserver(this);
}
~RemoveNotificationWaiter() override {
message_center::MessageCenter::Get()->RemoveObserver(this);
}
void Wait() { run_loop_.Run(); }
// message_center::MessageCenterObserver:
void OnNotificationRemoved(const std::string& notification_id,
const bool by_user) override {
if (notification_id == notification_id_) {
run_loop_.Quit();
}
}
private:
base::RunLoop run_loop_;
const std::string notification_id_;
};
} // namespace
class PrivacyHubCameraControllerTests : public AshTestBase {
protected:
PrivacyHubCameraControllerTests()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
scoped_feature_list_.InitAndEnableFeature(ash::features::kCrosPrivacyHub);
}
void SetUserPref(bool allowed) {
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
prefs::kUserCameraAllowed, allowed);
}
bool GetUserPref() const {
return Shell::Get()
->session_controller()
->GetActivePrefService()
->GetBoolean(prefs::kUserCameraAllowed);
}
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
auto mock_switch = std::make_unique<::testing::NiceMock<MockSwitchAPI>>();
mock_switch_ = mock_switch.get();
Shell::Get()->privacy_hub_controller()->set_frontend(&mock_frontend_);
controller_ = &Shell::Get()->privacy_hub_controller()->camera_controller();
controller_->SetCameraPrivacySwitchAPIForTest(std::move(mock_switch));
}
void LaunchAppAccessingCamera(const std::u16string& app_name) {
delegate_.LaunchAppAccessingCamera(app_name);
}
void WaitUntilNotificationRemoved(const std::string& notification_id) {
RemoveNotificationWaiter waiter{notification_id};
waiter.Wait();
}
::testing::NiceMock<MockFrontendAPI> mock_frontend_;
::testing::NiceMock<MockSwitchAPI>* mock_switch_;
CameraPrivacySwitchController* controller_;
base::test::ScopedFeatureList scoped_feature_list_;
const base::HistogramTester histogram_tester_;
FakeSensorDisabledNotificationDelegate delegate_;
};
// Test reaction on UI action.
TEST_F(PrivacyHubCameraControllerTests, UIAction) {
const std::vector<bool> user_pref_sequence{false, true, true, false, true};
const int number_of_changes = [&]() {
int cnt = 0;
bool current_val = true; // default value for camera-enabled is true
for (const bool pref_val : user_pref_sequence) {
if (pref_val != current_val) {
++cnt;
current_val = pref_val;
}
}
return cnt;
}();
CameraSWPrivacySwitchSetting captured_val;
EXPECT_CALL(*mock_switch_, SetCameraSWPrivacySwitch(_))
.Times(number_of_changes)
.WillRepeatedly(testing::SaveArg<0>(&captured_val));
for (const bool pref_val : user_pref_sequence) {
SetUserPref(pref_val);
// User toggle ON means the camera is DISABLED.
const CameraSWPrivacySwitchSetting expected_val =
pref_val ? CameraSWPrivacySwitchSetting::kEnabled
: CameraSWPrivacySwitchSetting::kDisabled;
EXPECT_EQ(captured_val, expected_val);
}
}
TEST_F(PrivacyHubCameraControllerTests, OnCameraSoftwarePrivacySwitchChanged) {
// When |prefs::kUserCameraAllowed| is true and CrOS Camera Service
// communicates the SW privacy switch state as UNKNOWN or ON, the states
// mismatch and SetCameraSWPrivacySwitch(kEnabled) should be called to correct
// the mismatch.
EXPECT_CALL(*mock_switch_,
SetCameraSWPrivacySwitch(CameraSWPrivacySwitchSetting::kEnabled))
.Times(::testing::Exactly(3));
SetUserPref(true);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::UNKNOWN);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::ON);
// When |prefs::kUserCameraAllowed| is false and CrOS Camera Service
// communicates the SW privacy switch state as UNKNOWN or OFF, the states
// mismatch and SetCameraSWPrivacySwitch(kDisabled) should be called to
// correct the mismatch.
EXPECT_CALL(*mock_switch_,
SetCameraSWPrivacySwitch(CameraSWPrivacySwitchSetting::kDisabled))
.Times(::testing::Exactly(3));
SetUserPref(false);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::UNKNOWN);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::OFF);
// When the SW privacy switch states match in Privacy Hub and CrOS Camera
// Service, SetCameraSWPrivacySwitch() should not be called.
EXPECT_CALL(*mock_switch_, SetCameraSWPrivacySwitch(_))
.Times(::testing::Exactly(2));
// When |prefs::kUserCameraAllowed| is true and CrOS Camera Service
// communicates the SW privacy switch state as OFF, the states match and
// SetCameraSWPrivacySwitch() should not be called.
SetUserPref(true);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::OFF);
// When |prefs::kUserCameraAllowed| is false and CrOS Camera Service
// communicates the SW privacy switch state as ON, the states match and
// SetCameraSWPrivacySwitch() should not be called.
SetUserPref(false);
controller_->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::ON);
}
TEST_F(PrivacyHubCameraControllerTests,
OnCameraHardwarePrivacySwitchChangedMultipleCameras) {
EXPECT_CALL(mock_frontend_, CameraHardwareToggleChanged(
cros::mojom::CameraPrivacySwitchState::OFF));
EXPECT_CALL(mock_frontend_, CameraHardwareToggleChanged(
cros::mojom::CameraPrivacySwitchState::ON));
CameraPrivacySwitchController& controller =
Shell::Get()->privacy_hub_controller()->camera_controller();
// We have 2 cameras in the system.
controller.OnCameraCountChanged(2);
// Camera is enabled in Privacy Hub.
SetUserPref(true);
// Somebody switched the camera off by the hardware switch.
controller.OnCameraHWPrivacySwitchStateChanged(
std::string(), cros::mojom::CameraPrivacySwitchState::OFF);
// Controller must know about it.
EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::OFF,
controller.HWSwitchState());
EXPECT_FALSE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
// Somebody switched the camera off by the hardware switch.
controller.OnCameraHWPrivacySwitchStateChanged(
std::string(), cros::mojom::CameraPrivacySwitchState::ON);
// Controller must know about it.
EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::ON,
controller.HWSwitchState());
message_center::MessageCenter* const message_center =
message_center::MessageCenter::Get();
// This particular notification ("Do you want to disable all cameras?") should
// appear only there are multiple cameras.
EXPECT_TRUE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
// User pref didn't change.
EXPECT_TRUE(GetUserPref());
// We didn't log any notification clicks so far.
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
0);
// Click on the notification button.
message_center->ClickOnNotificationButton(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId, 0);
// This must change the user pref for the camera (disabling all cameras).
EXPECT_FALSE(GetUserPref());
// The notification should be cleared after it has been clicked on.
EXPECT_FALSE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
// The histograms were updated.
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
1);
}
TEST_F(PrivacyHubCameraControllerTests,
OnCameraHardwarePrivacySwitchChangedOneCamera) {
EXPECT_CALL(mock_frontend_, CameraHardwareToggleChanged(
cros::mojom::CameraPrivacySwitchState::OFF));
EXPECT_CALL(mock_frontend_, CameraHardwareToggleChanged(
cros::mojom::CameraPrivacySwitchState::ON));
CameraPrivacySwitchController& controller =
Shell::Get()->privacy_hub_controller()->camera_controller();
// We have 1 camera in the system.
controller.OnCameraCountChanged(1);
// Camera is enabled in Privacy Hub.
SetUserPref(true);
// Somebody switched the camera off by the hardware switch.
controller.OnCameraHWPrivacySwitchStateChanged(
std::string(), cros::mojom::CameraPrivacySwitchState::OFF);
// Controller must know about it.
EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::OFF,
controller.HWSwitchState());
// This particular notification should appear only if there are multiple
// cameras.
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
// Switching the hardware switch back again.
controller.OnCameraHWPrivacySwitchStateChanged(
std::string(), cros::mojom::CameraPrivacySwitchState::ON);
// Controller is aware.
EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::ON,
controller.HWSwitchState());
// This didn't cause any change in the setting toggle.
EXPECT_TRUE(GetUserPref());
// There were no changes to the histograms.
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
0);
}
// This test is a regression test for b/253407315
TEST_F(PrivacyHubCameraControllerTests,
OnCameraHardwarePrivacySwitchChangedNotificationClearing) {
CameraPrivacySwitchController& controller =
Shell::Get()->privacy_hub_controller()->camera_controller();
SetUserPref(true);
controller.OnCameraCountChanged(2);
controller.OnCameraHWPrivacySwitchStateChanged(
"0", cros::mojom::CameraPrivacySwitchState::ON);
const message_center::Notification* const notification = FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId);
EXPECT_TRUE(notification);
// User should be able to clear the notification manually
EXPECT_FALSE(notification->rich_notification_data().pinned);
// Notification should be cleared when hardware mute is disabled
controller.OnCameraHWPrivacySwitchStateChanged(
"0", cros::mojom::CameraPrivacySwitchState::OFF);
WaitUntilNotificationRemoved(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId);
EXPECT_FALSE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
}
TEST_F(PrivacyHubCameraControllerTests,
CameraOffNotificationRemoveViaClickOnButton) {
SetUserPref(false);
message_center::MessageCenter* const message_center =
message_center::MessageCenter::Get();
ASSERT_TRUE(message_center);
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// An application starts accessing the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// A notification should be fired.
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_FALSE(GetUserPref());
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
// Enabling camera via clicking on the button should clear the notification
message_center->ClickOnNotificationButton(kPrivacyHubCameraOffNotificationId,
0);
EXPECT_TRUE(GetUserPref());
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
1);
}
TEST_F(PrivacyHubCameraControllerTests,
CameraOffNotificationRemoveViaClickOnBody) {
SetUserPref(false);
controller_->OnCameraCountChanged(2);
message_center::MessageCenter* const message_center =
message_center::MessageCenter::Get();
ASSERT_TRUE(message_center);
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// An application starts accessing the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// A notification should be fired.
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_FALSE(GetUserPref());
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
privacy_hub_metrics::PrivacyHubNavigationOrigin::kNotification),
0);
// Enabling camera via clicking on the body should open the privacy hub
// settings page.
message_center->ClickOnNotification(kPrivacyHubCameraOffNotificationId);
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
// The user pref should not be changed.
EXPECT_FALSE(GetUserPref());
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
privacy_hub_metrics::PrivacyHubNavigationOrigin::kNotification),
1);
SetUserPref(true);
ASSERT_FALSE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
// Flip the hardware switch.
Shell::Get()
->privacy_hub_controller()
->camera_controller()
.OnCameraHWPrivacySwitchStateChanged(
"0", cros::mojom::CameraPrivacySwitchState::ON);
// A notification should be fired.
EXPECT_TRUE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
EXPECT_TRUE(GetUserPref());
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
privacy_hub_metrics::PrivacyHubNavigationOrigin::kNotification),
1);
// Clicking on the body should open the privacy hub settings page.
message_center->ClickOnNotification(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId);
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 2);
// The user pref should not be changed.
EXPECT_TRUE(GetUserPref());
EXPECT_FALSE(FindNotificationById(
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
privacy_hub_metrics::PrivacyHubNavigationOrigin::kNotification),
2);
}
TEST_F(PrivacyHubCameraControllerTests,
CameraOffNotificationRemoveViaUserPref) {
SetUserPref(false);
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// An application starts accessing the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// A notification should be fired.
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_FALSE(GetUserPref());
// Enabling camera via the user pref should clear the notification
SetUserPref(true);
EXPECT_TRUE(GetUserPref());
WaitUntilNotificationRemoved(kPrivacyHubCameraOffNotificationId);
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
}
TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) {
SetUserPref(true);
message_center::MessageCenter* const message_center =
message_center::MessageCenter::Get();
ASSERT_TRUE(message_center);
message_center->RemoveNotification(kPrivacyHubCameraOffNotificationId, false);
// An application starts accessing the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// Disable camera
SetUserPref(false);
// A notification should be fired.
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_FALSE(GetUserPref());
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
// Enabling camera via clicking on the button should clear the notification
message_center->ClickOnNotificationButton(kPrivacyHubCameraOffNotificationId,
0);
EXPECT_TRUE(GetUserPref());
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
1);
}
// Tests if the notification `kPrivacyHubCameraOffNotificationId` is removed
// when the number of apps accessing the camera becomes 0.
TEST_F(PrivacyHubCameraControllerTests,
NotificationRemovedWhenNoActiveApplication) {
SetUserPref(true);
// The notification should not be in the message center initially.
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// This is the effect of an application starting to access the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// Disabling camera using the software switch.
SetUserPref(false);
// Notification `kPrivacyHubCameraOffNotificationId` should pop up.
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// The only active application stops accessing the camera the camera.
controller_->ActiveApplicationsChanged(/*application_added=*/false);
WaitUntilNotificationRemoved(kPrivacyHubCameraOffNotificationId);
// Existing notification `kPrivacyHubCameraOffNotificationId` should be
// removed as the number of active applications is 0 now.
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
}
// Tests if the camera software switch notification contains proper text.
TEST_F(PrivacyHubCameraControllerTests, NotificationText) {
SetUserPref(true);
// The notification should not be in the message center initially.
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
// This fakes launching an application with name "app_1_name".
LaunchAppAccessingCamera(u"app_1_name");
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// Disabling camera using the software switch.
SetUserPref(false);
// Notification should pop up. The notification body should contain the app
// name "app_1_name".
message_center::Notification* notification =
FindNotificationById(kPrivacyHubCameraOffNotificationId);
EXPECT_TRUE(notification);
EXPECT_EQ(
l10n_util::GetStringUTF16(IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_TITLE),
notification->title());
EXPECT_EQ(
l10n_util::GetStringFUTF16(
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME,
u"app_1_name"),
notification->message());
// This fakes launching another application with name "app_2_name".
LaunchAppAccessingCamera(u"app_2_name");
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// A new notification should pop up. The notification body should contain both
// the application names in order of most recently launched first.
notification = FindNotificationById(kPrivacyHubCameraOffNotificationId);
EXPECT_TRUE(notification);
EXPECT_EQ(
l10n_util::GetStringFUTF16(
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES,
u"app_2_name", u"app_1_name"),
notification->message());
// This fakes launching another application with name "app_3_name".
LaunchAppAccessingCamera(u"app_3_name");
controller_->ActiveApplicationsChanged(/*application_added=*/true);
// A new notification should pop up. The notification body should not contain
// any application name as there are more than 2 applications attempting to
// access camera.
notification = FindNotificationById(kPrivacyHubCameraOffNotificationId);
EXPECT_TRUE(notification);
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE),
notification->message());
}
TEST_F(PrivacyHubCameraControllerTests, MetricCollection) {
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
0);
CameraPrivacySwitchController::SetAndLogCameraPreferenceFromNotification(
false);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
0);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
1);
CameraPrivacySwitchController::SetAndLogCameraPreferenceFromNotification(
true);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
true),
1);
EXPECT_EQ(histogram_tester_.GetBucketCount(
privacy_hub_metrics::
kPrivacyHubCameraEnabledFromNotificationHistogram,
false),
1);
}
} // namespace ash