[Privacy Hub] Update camera software switch notification strings
This CL updtes the strings displayed in the camera software switch notifications. The notification message now may contain application names attempting to use camera depending on the number of applications. Please visit go/privacy-hub-strings for detailed specs. Bug: b:260233272 Change-Id: I00fd8b5759f4801fdba9b9855382145d2c2f0171 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4075631 Reviewed-by: James Cook <jamescook@chromium.org> Commit-Queue: Md Shahadat Hossain Shahin <shahinmd@google.com> Cr-Commit-Position: refs/heads/main@{#1080415}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
340ba316cb
commit
5ccffc9f62
ash
ash_strings.grd
ash_strings_grd
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE.png.sha1IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME.png.sha1IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES.png.sha1IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_TITLE.png.sha1
public
cpp
system
microphone_mute
privacy_hub
chrome/browser/ui/ash
@ -4512,10 +4512,16 @@ Here are some things you can try to get started.
|
||||
Turn off the camera access on this device to block all cameras all the time.
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the camera while the camera is disabled.">
|
||||
Camera access is off on this device
|
||||
Turn on camera access?
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE" desc="Message for a notification shown to the users when an app tries to use the camera while the camera is off.">
|
||||
Turn on camera access to allow apps and webpages with the camera permission to use the camera(s) connected to this device.
|
||||
This allows camera access for all apps and websites with the camera permission
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME" desc="Message for a notification shown to the users when an app tries to use the camera while the camera is off.">
|
||||
This allows camera access for <ph name="APP_NAME">$1<ex>Zoom</ex></ph> and all apps and websites with the camera permission
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES" desc="Message for a notification shown to the users when an app tries to use the camera while the camera is off.">
|
||||
This allows camera access for <ph name="APP1_NAME">$1<ex>Zoom</ex></ph>, <ph name="APP2_NAME">$2<ex>Snapchat</ex></ph>, and all apps and websites with the camera permission
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_HUB_TURN_OFF_CAMERA_ACTION_BUTTON" desc="Label for an action button that disables the camera.">
|
||||
Turn off camera access
|
||||
|
@ -1 +1 @@
|
||||
fec376b7f80d5acb18aa84cedc2165e2cdf3afe3
|
||||
282bb6502b72e7c61bbe55f9b7bd53db6feb5b7f
|
1
ash/ash_strings_grd/IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
1ae465c623bc10ac8bb198a12aba27490aedabff
|
1
ash/ash_strings_grd/IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
8fc68d9220d189810d5e24db80a5c116d43840c9
|
@ -1 +1 @@
|
||||
fec376b7f80d5acb18aa84cedc2165e2cdf3afe3
|
||||
1ae465c623bc10ac8bb198a12aba27490aedabff
|
@ -210,8 +210,6 @@ component("cpp") {
|
||||
"message_center_ash.h",
|
||||
"metrics_util.cc",
|
||||
"metrics_util.h",
|
||||
"microphone_mute_notification_delegate.cc",
|
||||
"microphone_mute_notification_delegate.h",
|
||||
"nearby_share_controller.h",
|
||||
"nearby_share_delegate.h",
|
||||
"network_config_service.cc",
|
||||
@ -275,6 +273,8 @@ component("cpp") {
|
||||
"screen_backlight_observer.h",
|
||||
"screen_backlight_type.h",
|
||||
"select_to_speak_event_handler_delegate.h",
|
||||
"sensor_disabled_notification_delegate.cc",
|
||||
"sensor_disabled_notification_delegate.h",
|
||||
"session/session_activation_observer.h",
|
||||
"session/session_controller.cc",
|
||||
"session/session_controller.h",
|
||||
|
@ -1,36 +0,0 @@
|
||||
// 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 ASH_PUBLIC_CPP_MICROPHONE_MUTE_NOTIFICATION_DELEGATE_H_
|
||||
#define ASH_PUBLIC_CPP_MICROPHONE_MUTE_NOTIFICATION_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ash/public/cpp/ash_public_export.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// This delegate exists so that code relevant to microphone mute notifications
|
||||
// under //ash can call back into //chrome. The actual delegate instance is
|
||||
// owned and constructed by code in //chrome during startup.
|
||||
class ASH_PUBLIC_EXPORT MicrophoneMuteNotificationDelegate {
|
||||
public:
|
||||
static MicrophoneMuteNotificationDelegate* Get();
|
||||
|
||||
// Returns a list of names of the applications that have attempted to use the
|
||||
// microphone, in order of most-recently-launched. If an application is
|
||||
// accessing the microphone but no name could be determined, the name of that
|
||||
// application will not be in the returned list.
|
||||
virtual std::vector<std::u16string> GetAppsAccessingMicrophone() = 0;
|
||||
|
||||
protected:
|
||||
MicrophoneMuteNotificationDelegate();
|
||||
virtual ~MicrophoneMuteNotificationDelegate();
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_PUBLIC_CPP_MICROPHONE_MUTE_NOTIFICATION_DELEGATE_H_
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
@ -11,21 +11,21 @@ namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
MicrophoneMuteNotificationDelegate* g_instance = nullptr;
|
||||
SensorDisabledNotificationDelegate* g_instance = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
MicrophoneMuteNotificationDelegate* MicrophoneMuteNotificationDelegate::Get() {
|
||||
SensorDisabledNotificationDelegate* SensorDisabledNotificationDelegate::Get() {
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
MicrophoneMuteNotificationDelegate::MicrophoneMuteNotificationDelegate() {
|
||||
SensorDisabledNotificationDelegate::SensorDisabledNotificationDelegate() {
|
||||
DCHECK(!g_instance);
|
||||
g_instance = this;
|
||||
}
|
||||
|
||||
MicrophoneMuteNotificationDelegate::~MicrophoneMuteNotificationDelegate() {
|
||||
SensorDisabledNotificationDelegate::~SensorDisabledNotificationDelegate() {
|
||||
DCHECK_EQ(this, g_instance);
|
||||
g_instance = nullptr;
|
||||
}
|
42
ash/public/cpp/sensor_disabled_notification_delegate.h
Normal file
42
ash/public/cpp/sensor_disabled_notification_delegate.h
Normal file
@ -0,0 +1,42 @@
|
||||
// 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 ASH_PUBLIC_CPP_SENSOR_DISABLED_NOTIFICATION_DELEGATE_H_
|
||||
#define ASH_PUBLIC_CPP_SENSOR_DISABLED_NOTIFICATION_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ash/public/cpp/ash_public_export.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// This delegate exists so that code relevant to sensor (microphone and camera)
|
||||
// disabled notifications under //ash can call back into //chrome. The actual
|
||||
// delegate instance is owned and constructed by code in //chrome during
|
||||
// startup.
|
||||
class ASH_PUBLIC_EXPORT SensorDisabledNotificationDelegate {
|
||||
public:
|
||||
static SensorDisabledNotificationDelegate* Get();
|
||||
|
||||
enum class Sensor {
|
||||
kCamera,
|
||||
kMicrophone,
|
||||
};
|
||||
|
||||
// Returns a list of names of the applications that have attempted to use the
|
||||
// sensor (camera or microphone), in order of most-recently-launched. If an
|
||||
// application is accessing the sensor but no name could be determined, the
|
||||
// name of that application will not be in the returned list.
|
||||
virtual std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) = 0;
|
||||
|
||||
protected:
|
||||
SensorDisabledNotificationDelegate();
|
||||
virtual ~SensorDisabledNotificationDelegate();
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_PUBLIC_CPP_SENSOR_DISABLED_NOTIFICATION_DELEGATE_H_
|
@ -9,8 +9,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/notifier_catalogs.h"
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/notification_utils.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/privacy_hub/privacy_hub_metrics.h"
|
||||
@ -88,13 +88,15 @@ void MicrophoneMuteNotificationController::MaybeShowNotification(
|
||||
message_center::NotificationPriority priority,
|
||||
bool recreate) {
|
||||
if (mic_mute_on_) {
|
||||
auto* microphone_mute_notification_delegate =
|
||||
MicrophoneMuteNotificationDelegate::Get();
|
||||
// `MicrophoneMuteNotificationDelegate` is not created in guest mode.
|
||||
if (!microphone_mute_notification_delegate)
|
||||
auto* sensor_disabled_notification_delegate =
|
||||
SensorDisabledNotificationDelegate::Get();
|
||||
// `SensorDisabledNotificationDelegate` is not created in guest mode.
|
||||
if (!sensor_disabled_notification_delegate) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::u16string> app_names =
|
||||
microphone_mute_notification_delegate->GetAppsAccessingMicrophone();
|
||||
sensor_disabled_notification_delegate->GetAppsAccessingSensor(
|
||||
SensorDisabledNotificationDelegate::Sensor::kMicrophone);
|
||||
if (!app_names.empty() || input_stream_count_) {
|
||||
if (recreate)
|
||||
RemoveMicrophoneMuteNotification();
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/public/cpp/test/test_system_tray_client.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/microphone_mute/microphone_mute_notification_controller.h"
|
||||
@ -26,20 +26,26 @@
|
||||
|
||||
namespace ash {
|
||||
|
||||
class FakeMicrophoneMuteNotificationDelegate
|
||||
: public MicrophoneMuteNotificationDelegate {
|
||||
class FakeSensorDisabledNotificationDelegate
|
||||
: public SensorDisabledNotificationDelegate {
|
||||
public:
|
||||
std::vector<std::u16string> GetAppsAccessingMicrophone() override {
|
||||
return app_names_;
|
||||
std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override {
|
||||
if (sensor == Sensor::kMicrophone) {
|
||||
return apps_accessing_microphone_;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void AddAppAccessingMicrophone(
|
||||
void LaunchAppAccessingMicrophone(
|
||||
const absl::optional<std::u16string> app_name) {
|
||||
if (app_name.has_value())
|
||||
app_names_.insert(app_names_.begin(), app_name.value());
|
||||
if (app_name.has_value()) {
|
||||
apps_accessing_microphone_.insert(apps_accessing_microphone_.begin(),
|
||||
app_name.value());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::u16string> app_names_;
|
||||
private:
|
||||
std::vector<std::u16string> apps_accessing_microphone_;
|
||||
};
|
||||
|
||||
class MicrophoneMuteNotificationControllerTest : public AshTestBase {
|
||||
@ -53,7 +59,7 @@ class MicrophoneMuteNotificationControllerTest : public AshTestBase {
|
||||
void SetUp() override {
|
||||
AshTestBase::SetUp();
|
||||
controller_ = std::make_unique<MicrophoneMuteNotificationController>();
|
||||
delegate_ = std::make_unique<FakeMicrophoneMuteNotificationDelegate>();
|
||||
delegate_ = std::make_unique<FakeSensorDisabledNotificationDelegate>();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@ -124,7 +130,7 @@ class MicrophoneMuteNotificationControllerTest : public AshTestBase {
|
||||
}
|
||||
|
||||
void LaunchApp(absl::optional<std::u16string> app_name) {
|
||||
delegate_->AddAppAccessingMicrophone(app_name);
|
||||
delegate_->LaunchAppAccessingMicrophone(app_name);
|
||||
}
|
||||
|
||||
const base::HistogramTester& histogram_tester() const {
|
||||
@ -135,7 +141,7 @@ class MicrophoneMuteNotificationControllerTest : public AshTestBase {
|
||||
const base::HistogramTester histogram_tester_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
std::unique_ptr<MicrophoneMuteNotificationController> controller_;
|
||||
std::unique_ptr<FakeMicrophoneMuteNotificationDelegate> delegate_;
|
||||
std::unique_ptr<FakeSensorDisabledNotificationDelegate> delegate_;
|
||||
};
|
||||
|
||||
TEST_F(MicrophoneMuteNotificationControllerTest, SimpleMuteUnMute) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/constants/notifier_catalogs.h"
|
||||
#include "ash/public/cpp/notification_utils.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/public/cpp/session/session_observer.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
@ -192,11 +193,33 @@ CameraPrivacySwitchController::HWSwitchState() const {
|
||||
return camera_privacy_switch_state_;
|
||||
}
|
||||
|
||||
std::u16string
|
||||
CameraPrivacySwitchController::GetCameraOffNotificationMessage() {
|
||||
auto* sensor_disabled_notification_delegate =
|
||||
SensorDisabledNotificationDelegate::Get();
|
||||
std::vector<std::u16string> app_names =
|
||||
sensor_disabled_notification_delegate->GetAppsAccessingSensor(
|
||||
SensorDisabledNotificationDelegate::Sensor::kCamera);
|
||||
|
||||
if (app_names.size() == 1) {
|
||||
return l10n_util::GetStringFUTF16(
|
||||
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_ONE_APP_NAME,
|
||||
app_names[0]);
|
||||
} else if (app_names.size() == 2) {
|
||||
return l10n_util::GetStringFUTF16(
|
||||
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE_WITH_TWO_APP_NAMES,
|
||||
app_names[0], app_names[1]);
|
||||
}
|
||||
|
||||
return l10n_util::GetStringUTF16(
|
||||
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE);
|
||||
}
|
||||
|
||||
void CameraPrivacySwitchController::ShowCameraOffNotification() {
|
||||
ShowNotification(/*action_enables_camera=*/true,
|
||||
kPrivacyHubCameraOffNotificationId,
|
||||
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_TITLE,
|
||||
IDS_PRIVACY_HUB_CAMERA_OFF_NOTIFICATION_MESSAGE,
|
||||
GetCameraOffNotificationMessage(),
|
||||
ash::NotificationCatalogName::kPrivacyHubCamera);
|
||||
}
|
||||
|
||||
@ -206,7 +229,8 @@ void CameraPrivacySwitchController::
|
||||
/*action_enables_camera=*/false,
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId,
|
||||
IDS_PRIVACY_HUB_WANT_TO_TURN_OFF_CAMERA_NOTIFICATION_TITLE,
|
||||
IDS_PRIVACY_HUB_WANT_TO_TURN_OFF_CAMERA_NOTIFICATION_MESSAGE,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_PRIVACY_HUB_WANT_TO_TURN_OFF_CAMERA_NOTIFICATION_MESSAGE),
|
||||
NotificationCatalogName::kPrivacyHubHWCameraSwitchOffSWCameraSwitchOn);
|
||||
}
|
||||
|
||||
@ -214,7 +238,7 @@ void CameraPrivacySwitchController::ShowNotification(
|
||||
bool action_enables_camera,
|
||||
const char* kNotificationId,
|
||||
const int notification_title_id,
|
||||
const int notification_message_id,
|
||||
const std::u16string& notification_message,
|
||||
const NotificationCatalogName catalog) {
|
||||
message_center::RichNotificationData notification_data;
|
||||
notification_data.pinned = false;
|
||||
@ -242,7 +266,7 @@ void CameraPrivacySwitchController::ShowNotification(
|
||||
ash::CreateSystemNotificationPtr(
|
||||
message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
|
||||
l10n_util::GetStringUTF16(notification_title_id),
|
||||
l10n_util::GetStringUTF16(notification_message_id),
|
||||
notification_message,
|
||||
/*display_source=*/std::u16string(),
|
||||
/*origin_url=*/GURL(),
|
||||
message_center::NotifierId(
|
||||
|
@ -95,11 +95,15 @@ class ASH_EXPORT CameraPrivacySwitchController
|
||||
// Displays the "Do you want to turn the camera off" notification.
|
||||
void ShowHWCameraSwitchOffSWCameraSwitchOnNotification();
|
||||
|
||||
// A helper to generate the message to display in the camera software switch
|
||||
// notification.
|
||||
std::u16string GetCameraOffNotificationMessage();
|
||||
|
||||
// Displays a notification with an action that can enable/disable the camera.
|
||||
void ShowNotification(bool action_enables_camera,
|
||||
const char* kNotificationId,
|
||||
const int notification_title_id,
|
||||
const int notification_message_id,
|
||||
const std::u16string& notification_message,
|
||||
const NotificationCatalogName catalog);
|
||||
|
||||
// Clears all notifications related to the camera SW switch
|
||||
|
@ -8,9 +8,11 @@
|
||||
#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"
|
||||
@ -20,7 +22,9 @@
|
||||
#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/public/cpp/notification.h"
|
||||
|
||||
using testing::_;
|
||||
|
||||
@ -46,6 +50,28 @@ class MockFrontendAPI : public PrivacyHubDelegate {
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class PrivacyHubCameraControllerTests : public AshTestBase {
|
||||
@ -78,11 +104,16 @@ class PrivacyHubCameraControllerTests : public AshTestBase {
|
||||
controller_->SetCameraPrivacySwitchAPIForTest(std::move(mock_switch));
|
||||
}
|
||||
|
||||
void LaunchAppAccessingCamera(const std::u16string& app_name) {
|
||||
delegate_.LaunchAppAccessingCamera(app_name);
|
||||
}
|
||||
|
||||
::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.
|
||||
@ -175,7 +206,7 @@ TEST_F(PrivacyHubCameraControllerTests, OnCameraHardwarePrivacySwitchChanged) {
|
||||
std::string(), cros::mojom::CameraPrivacySwitchState::OFF);
|
||||
EXPECT_EQ(cros::mojom::CameraPrivacySwitchState::OFF,
|
||||
controller.HWSwitchState());
|
||||
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
EXPECT_FALSE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
|
||||
controller.OnCameraHWPrivacySwitchStateChanged(
|
||||
@ -185,7 +216,7 @@ TEST_F(PrivacyHubCameraControllerTests, OnCameraHardwarePrivacySwitchChanged) {
|
||||
|
||||
message_center::MessageCenter* const message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
EXPECT_TRUE(message_center->FindNotificationById(
|
||||
EXPECT_TRUE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
@ -201,7 +232,7 @@ TEST_F(PrivacyHubCameraControllerTests, OnCameraHardwarePrivacySwitchChanged) {
|
||||
message_center->ClickOnNotificationButton(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId, 0);
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
EXPECT_FALSE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
privacy_hub_metrics::
|
||||
@ -224,16 +255,15 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
|
||||
controller.OnCameraHWPrivacySwitchStateChanged(
|
||||
"0", cros::mojom::CameraPrivacySwitchState::ON);
|
||||
const message_center::Notification* const notification =
|
||||
message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId);
|
||||
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);
|
||||
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
EXPECT_FALSE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
}
|
||||
|
||||
@ -243,14 +273,12 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
message_center::MessageCenter* const message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
ASSERT_TRUE(message_center);
|
||||
ASSERT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
|
||||
// An application starts accessing the camera.
|
||||
controller_->ActiveApplicationsChanged(/*application_added=*/true);
|
||||
// A notification should be fired.
|
||||
EXPECT_TRUE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
@ -262,8 +290,7 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
message_center->ClickOnNotificationButton(kPrivacyHubCameraOffNotificationId,
|
||||
0);
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
privacy_hub_metrics::
|
||||
kPrivacyHubCameraEnabledFromNotificationHistogram,
|
||||
@ -277,14 +304,12 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
message_center::MessageCenter* const message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
ASSERT_TRUE(message_center);
|
||||
ASSERT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
|
||||
// An application starts accessing the camera.
|
||||
controller_->ActiveApplicationsChanged(/*application_added=*/true);
|
||||
// A notification should be fired.
|
||||
EXPECT_TRUE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
|
||||
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 0);
|
||||
@ -300,8 +325,7 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 1);
|
||||
// The user pref should not be changed.
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
|
||||
privacy_hub_metrics::PrivacyHubNavigationOrigin::kNotification),
|
||||
@ -309,7 +333,7 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
|
||||
SetUserPref(true);
|
||||
|
||||
ASSERT_FALSE(message_center->FindNotificationById(
|
||||
ASSERT_FALSE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
|
||||
// Flip the hardware switch.
|
||||
@ -320,7 +344,7 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
"0", cros::mojom::CameraPrivacySwitchState::ON);
|
||||
|
||||
// A notification should be fired.
|
||||
EXPECT_TRUE(message_center->FindNotificationById(
|
||||
EXPECT_TRUE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
|
||||
@ -337,7 +361,7 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
EXPECT_EQ(GetSystemTrayClient()->show_os_settings_privacy_hub_count(), 2);
|
||||
// The user pref should not be changed.
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
EXPECT_FALSE(message_center->FindNotificationById(
|
||||
EXPECT_FALSE(FindNotificationById(
|
||||
kPrivacyHubHWCameraSwitchOffSWCameraSwitchOnNotificationId));
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
privacy_hub_metrics::kPrivacyHubOpenedHistogram,
|
||||
@ -348,24 +372,18 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
TEST_F(PrivacyHubCameraControllerTests,
|
||||
CameraOffNotificationRemoveViaUserPref) {
|
||||
SetUserPref(false);
|
||||
message_center::MessageCenter* const message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
ASSERT_TRUE(message_center);
|
||||
ASSERT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
ASSERT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
|
||||
// An application starts accessing the camera.
|
||||
controller_->ActiveApplicationsChanged(/*application_added=*/true);
|
||||
// A notification should be fired.
|
||||
EXPECT_TRUE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
|
||||
// Enabling camera via the user pref should clear the notification
|
||||
SetUserPref(true);
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
}
|
||||
|
||||
TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) {
|
||||
@ -381,8 +399,7 @@ TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) {
|
||||
SetUserPref(false);
|
||||
|
||||
// A notification should be fired.
|
||||
EXPECT_TRUE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(GetUserPref());
|
||||
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
@ -394,8 +411,7 @@ TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) {
|
||||
message_center->ClickOnNotificationButton(kPrivacyHubCameraOffNotificationId,
|
||||
0);
|
||||
EXPECT_TRUE(GetUserPref());
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_EQ(histogram_tester_.GetBucketCount(
|
||||
privacy_hub_metrics::
|
||||
kPrivacyHubCameraEnabledFromNotificationHistogram,
|
||||
@ -408,13 +424,9 @@ TEST_F(PrivacyHubCameraControllerTests, InSessionSwitchNotification) {
|
||||
TEST_F(PrivacyHubCameraControllerTests,
|
||||
NotificationRemovedWhenNoActiveApplication) {
|
||||
SetUserPref(true);
|
||||
message_center::MessageCenter* const message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
ASSERT_TRUE(message_center);
|
||||
|
||||
// The notification should not be in the message center initially.
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_FALSE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
|
||||
// This is the effect of an application starting to access the camera.
|
||||
controller_->ActiveApplicationsChanged(/*application_added=*/true);
|
||||
@ -423,16 +435,70 @@ TEST_F(PrivacyHubCameraControllerTests,
|
||||
SetUserPref(false);
|
||||
|
||||
// Notification `kPrivacyHubCameraOffNotificationId` should pop up.
|
||||
EXPECT_TRUE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
EXPECT_TRUE(FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
|
||||
// The only active application stops accessing the camera the camera.
|
||||
controller_->ActiveApplicationsChanged(/*application_added=*/false);
|
||||
|
||||
// Existing notification `kPrivacyHubCameraOffNotificationId` should be
|
||||
// removed as the number of active applications is 0 now.
|
||||
EXPECT_FALSE(
|
||||
message_center->FindNotificationById(kPrivacyHubCameraOffNotificationId));
|
||||
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) {
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/privacy_hub_delegate.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/privacy_hub/privacy_hub_controller.h"
|
||||
@ -26,10 +26,10 @@ using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
class FakeMicrophoneMuteNotificationDelegate
|
||||
: public MicrophoneMuteNotificationDelegate {
|
||||
class FakeSensorDisabledNotificationDelegate
|
||||
: public SensorDisabledNotificationDelegate {
|
||||
public:
|
||||
std::vector<std::u16string> GetAppsAccessingMicrophone() override {
|
||||
std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@ -55,9 +55,9 @@ class PrivacyHubMicrophoneControllerTest : public AshTestBase {
|
||||
void SetUp() override {
|
||||
AshTestBase::SetUp();
|
||||
|
||||
// This makes sure a global instance of MicrophoneMuteNotificationDelegate
|
||||
// This makes sure a global instance of SensorDisabledNotificationDelegate
|
||||
// is created before running tests.
|
||||
delegate_ = std::make_unique<FakeMicrophoneMuteNotificationDelegate>();
|
||||
delegate_ = std::make_unique<FakeSensorDisabledNotificationDelegate>();
|
||||
Shell::Get()->privacy_hub_controller()->set_frontend(&mock_frontend_);
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ class PrivacyHubMicrophoneControllerTest : public AshTestBase {
|
||||
::testing::NiceMock<MockFrontendAPI> mock_frontend_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FakeMicrophoneMuteNotificationDelegate> delegate_;
|
||||
std::unique_ptr<FakeSensorDisabledNotificationDelegate> delegate_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/public/cpp/test/test_system_tray_client.h"
|
||||
#include "ash/shelf/shelf.h"
|
||||
#include "ash/shell.h"
|
||||
@ -28,10 +28,10 @@
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
class FakeMicrophoneMuteNotificationDelegate
|
||||
: public MicrophoneMuteNotificationDelegate {
|
||||
class FakeSensorDisabledNotificationDelegate
|
||||
: public SensorDisabledNotificationDelegate {
|
||||
public:
|
||||
std::vector<std::u16string> GetAppsAccessingMicrophone() override {
|
||||
std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@ -55,7 +55,6 @@ class PrivacyHubNotificationControllerTest : public AshTestBase {
|
||||
Shell::Get()->system_notification_controller()->microphone_mute_.get();
|
||||
controller_ =
|
||||
Shell::Get()->system_notification_controller()->privacy_hub_.get();
|
||||
;
|
||||
}
|
||||
void TearDown() override { AshTestBase::TearDown(); }
|
||||
|
||||
@ -128,7 +127,7 @@ class PrivacyHubNotificationControllerTest : public AshTestBase {
|
||||
|
||||
private:
|
||||
base::raw_ptr<PrivacyHubNotificationController> controller_;
|
||||
const FakeMicrophoneMuteNotificationDelegate delegate_;
|
||||
const FakeSensorDisabledNotificationDelegate delegate_;
|
||||
const base::HistogramTester histogram_tester_;
|
||||
base::raw_ptr<MicrophoneMuteNotificationController>
|
||||
microphone_mute_controller_;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/privacy/privacy_indicators_controller.h"
|
||||
#include "ash/system/privacy_hub/camera_privacy_switch_controller.h"
|
||||
@ -42,12 +43,22 @@ apps::AppCapabilityAccessCache* GetAppCapabilityAccessCache(
|
||||
absl::optional<std::u16string> MapAppIdToShortName(
|
||||
std::string app_id,
|
||||
apps::AppCapabilityAccessCache* capability_cache,
|
||||
apps::AppRegistryCache* registry_cache) {
|
||||
apps::AppRegistryCache* registry_cache,
|
||||
ash::SensorDisabledNotificationDelegate::Sensor sensor) {
|
||||
DCHECK(capability_cache);
|
||||
DCHECK(registry_cache);
|
||||
|
||||
for (const std::string& app :
|
||||
capability_cache->GetAppsAccessingMicrophone()) {
|
||||
std::set<std::string> apps_accessing_sensor;
|
||||
switch (sensor) {
|
||||
case ash::SensorDisabledNotificationDelegate::Sensor::kCamera:
|
||||
apps_accessing_sensor = capability_cache->GetAppsAccessingCamera();
|
||||
break;
|
||||
case ash::SensorDisabledNotificationDelegate::Sensor::kMicrophone:
|
||||
apps_accessing_sensor = capability_cache->GetAppsAccessingMicrophone();
|
||||
break;
|
||||
}
|
||||
|
||||
for (const std::string& app : apps_accessing_sensor) {
|
||||
absl::optional<std::u16string> name;
|
||||
registry_cache->ForOneApp(app,
|
||||
[&app_id, &name](const apps::AppUpdate& update) {
|
||||
@ -98,22 +109,34 @@ AppAccessNotifier::AppAccessNotifier() {
|
||||
|
||||
AppAccessNotifier::~AppAccessNotifier() = default;
|
||||
|
||||
std::vector<std::u16string> AppAccessNotifier::GetAppsAccessingMicrophone() {
|
||||
std::vector<std::u16string> AppAccessNotifier::GetAppsAccessingSensor(
|
||||
ash::SensorDisabledNotificationDelegate::Sensor sensor) {
|
||||
apps::AppRegistryCache* reg_cache = GetActiveUserAppRegistryCache();
|
||||
|
||||
apps::AppCapabilityAccessCache* cap_cache =
|
||||
GetActiveUserAppCapabilityAccessCache();
|
||||
|
||||
MruAppIdList* app_id_list;
|
||||
switch (sensor) {
|
||||
case ash::SensorDisabledNotificationDelegate::Sensor::kCamera:
|
||||
app_id_list = &camera_using_app_ids_[active_user_account_id_];
|
||||
break;
|
||||
case ash::SensorDisabledNotificationDelegate::Sensor::kMicrophone:
|
||||
app_id_list = &mic_using_app_ids_[active_user_account_id_];
|
||||
break;
|
||||
}
|
||||
|
||||
// A reg_cache and/or cap_cache of value nullptr is possible if we have no
|
||||
// active user, e.g. the login screen, so we test and return empty list in
|
||||
// that case instead of using DCHECK().
|
||||
if (!reg_cache || !cap_cache ||
|
||||
mic_using_app_ids_[active_user_account_id_].empty()) {
|
||||
if (!reg_cache || !cap_cache || app_id_list->empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::u16string> app_names;
|
||||
for (const auto& app_id : mic_using_app_ids_[active_user_account_id_]) {
|
||||
for (const auto& app_id : *app_id_list) {
|
||||
absl::optional<std::u16string> app_name =
|
||||
MapAppIdToShortName(app_id, cap_cache, reg_cache);
|
||||
MapAppIdToShortName(app_id, cap_cache, reg_cache, sensor);
|
||||
if (app_name.has_value())
|
||||
app_names.push_back(app_name.value());
|
||||
}
|
||||
@ -123,8 +146,9 @@ std::vector<std::u16string> AppAccessNotifier::GetAppsAccessingMicrophone() {
|
||||
bool AppAccessNotifier::MapContainsAppId(const MruAppIdMap& id_map,
|
||||
const std::string& app_id) {
|
||||
auto it = id_map.find(active_user_account_id_);
|
||||
if (it == id_map.end())
|
||||
if (it == id_map.end()) {
|
||||
return false;
|
||||
}
|
||||
return base::Contains(it->second, app_id);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ash/public/cpp/microphone_mute_notification_delegate.h"
|
||||
#include "ash/public/cpp/sensor_disabled_notification_delegate.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
@ -31,11 +31,11 @@ class SessionManager;
|
||||
|
||||
// This class is responsible for observing AppCapabilityAccessCache, notifying
|
||||
// to appropriate entities when an app is accessing camera/microphone. This is
|
||||
// also the concrete implementation of MicrophoneMuteNotificationDelegate, which
|
||||
// allows code relevant to microphone mute notifications that resides under
|
||||
// //ash to invoke functions/objects that actually reside under //chrome.
|
||||
// also the concrete implementation of SensorDisabledNotificationDelegate, which
|
||||
// allows code relevant to microphone and camera notifications that resides
|
||||
// under //ash to invoke functions/objects that actually reside under //chrome.
|
||||
class AppAccessNotifier
|
||||
: public ash::MicrophoneMuteNotificationDelegate,
|
||||
: public ash::SensorDisabledNotificationDelegate,
|
||||
public apps::AppCapabilityAccessCache::Observer,
|
||||
public session_manager::SessionManagerObserver,
|
||||
public user_manager::UserManager::UserSessionStateObserver {
|
||||
@ -45,8 +45,8 @@ class AppAccessNotifier
|
||||
AppAccessNotifier& operator=(const AppAccessNotifier&) = delete;
|
||||
~AppAccessNotifier() override;
|
||||
|
||||
// ash::MicrophoneMuteNotificationDelegate
|
||||
std::vector<std::u16string> GetAppsAccessingMicrophone() override;
|
||||
// ash::SensorDisabledNotificationDelegate
|
||||
std::vector<std::u16string> GetAppsAccessingSensor(Sensor sensor) override;
|
||||
|
||||
// apps::AppCapabilityAccessCache::Observer
|
||||
void OnCapabilityAccessUpdate(
|
||||
|
@ -147,8 +147,14 @@ class AppAccessNotifierBaseTest : public testing::Test {
|
||||
SetActiveUserAccountId(/*is_primary=*/false);
|
||||
}
|
||||
|
||||
std::vector<std::u16string> GetAppsAccessingCamera() {
|
||||
return app_access_notifier_->GetAppsAccessingSensor(
|
||||
ash::SensorDisabledNotificationDelegate::Sensor::kCamera);
|
||||
}
|
||||
|
||||
std::vector<std::u16string> GetAppsAccessingMicrophone() {
|
||||
return app_access_notifier_->GetAppsAccessingMicrophone();
|
||||
return app_access_notifier_->GetAppsAccessingSensor(
|
||||
ash::SensorDisabledNotificationDelegate::Sensor::kMicrophone);
|
||||
}
|
||||
|
||||
static apps::AppPtr MakeApp(const std::string app_id,
|
||||
@ -299,64 +305,85 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
/*IsCrosPrivacyHubEnabled()=*/testing::Bool()));
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, NoAppsLaunched) {
|
||||
// Should return a empty list of application names.
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_TRUE(app_names.empty());
|
||||
// The list of apps using mic or camera should be empty.
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, AppLaunchedNotUsingMicrophone) {
|
||||
TEST_P(AppAccessNotifierParameterizedTest, AppLaunchedNotUsingCameraAndMic) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
|
||||
// Should return a empty list of application names.
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_TRUE(app_names.empty());
|
||||
// The list of apps using mic or camera should be empty.
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, AppLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
TEST_P(AppAccessNotifierParameterizedTest, AppLaunchedUsingCameraAndMic) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// Returned list of application names should only contain "name_rose".
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_rose");
|
||||
// List of application names should only contain "name_rose".
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_rose"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_rose"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest,
|
||||
MultipleAppsLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
MultipleAppsLaunchedUsingBothCameraAndMic) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_mars", "name_mars", /*use_camera=*/false,
|
||||
LaunchAppUsingCameraOrMicrophone("id_mars", "name_mars", /*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_zara", "name_zara", /*use_camera=*/false,
|
||||
LaunchAppUsingCameraOrMicrophone("id_zara", "name_zara", /*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false);
|
||||
|
||||
// Most recently launched mic-using app should be in front of the returned
|
||||
// list.
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 3u);
|
||||
EXPECT_EQ(app_names[0], u"name_zara");
|
||||
// Only the applications using the sensor should be in the respective list and
|
||||
// the list should be ordered by the most recently launced app first.
|
||||
// "name_oscar" should not be in any of the lists.
|
||||
EXPECT_EQ(
|
||||
std::vector<std::u16string>({u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(
|
||||
std::vector<std::u16string>({u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Oscar starts using the mic, Oscar should be the front element of the
|
||||
// returned list now.
|
||||
// Oscar starts using camera, Oscar should be the front element of the camera
|
||||
// list now. The mic list should stay the same.
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/true);
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 4u);
|
||||
EXPECT_EQ(app_names[0], u"name_oscar");
|
||||
"id_oscar", "name_oscar", /*use_camera=*/true, /*use_microphone=*/false);
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_oscar", u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(
|
||||
std::vector<std::u16string>({u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Oscar starts using mic, Oscar should be the front element of the mic list
|
||||
// as well now.
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/true, /*use_microphone=*/true);
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_oscar", u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_oscar", u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// If we "kill" Oscar (set to no longer be using the mic or camera), Oscar
|
||||
// should not be in the returned list anymore. Zara should be at the front.
|
||||
// should not be in the returned lists anymore. Zara should be at the front of
|
||||
// both of the lists.
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false);
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 3u);
|
||||
EXPECT_TRUE(std::find(app_names.begin(), app_names.end(), u"name_oscar") ==
|
||||
app_names.end());
|
||||
EXPECT_EQ(app_names[0], u"name_zara");
|
||||
EXPECT_EQ(
|
||||
std::vector<std::u16string>({u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(
|
||||
std::vector<std::u16string>({u"name_zara", u"name_mars", u"name_rose"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, MultipleUsers) {
|
||||
@ -366,30 +393,32 @@ TEST_P(AppAccessNotifierParameterizedTest, MultipleUsers) {
|
||||
// Primary user is the active user.
|
||||
SetActiveUserAccountId(/*is_primary=*/true);
|
||||
|
||||
// Primary user launches a mic-using app.
|
||||
// Primary user launches an app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should be the front and only element of the returned
|
||||
// list.
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_primary_user");
|
||||
// App we just launched should be the front and only element of both the
|
||||
// lists.
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_primary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_primary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Secondary user is now the primary user.
|
||||
SetActiveUserAccountId(/*is_primary=*/false);
|
||||
|
||||
// Secondary user launches a mic-using app.
|
||||
// Secondary user launches an app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should be the front and only element of the returned
|
||||
// list.
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_secondary_user");
|
||||
// App we just launched should be the front and only element of both the
|
||||
// lists.
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Switch back to the primary user and "kill" the app it was running, no app
|
||||
// name to show.
|
||||
@ -397,23 +426,24 @@ TEST_P(AppAccessNotifierParameterizedTest, MultipleUsers) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_TRUE(app_names.empty());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingMicrophone());
|
||||
|
||||
// Now switch back to the secondary user, verify that the same app as before
|
||||
// shows up at the front of the returned list.
|
||||
// shows up in the lists.
|
||||
SetActiveUserAccountId(/*is_primary=*/false);
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_secondary_user");
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Now "kill" our secondary user's app and verify that there's no name to
|
||||
// show.
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_TRUE(app_names.empty());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, MultipleUsersMultipleApps) {
|
||||
@ -423,65 +453,72 @@ TEST_P(AppAccessNotifierParameterizedTest, MultipleUsersMultipleApps) {
|
||||
// Primary user is the active user.
|
||||
SetActiveUserAccountId(/*is_primary=*/true);
|
||||
|
||||
// Primary user launches a mic-using app.
|
||||
// Primary user launches an app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should be the front and only element of the returned
|
||||
// list.
|
||||
std::vector<std::u16string> app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_primary_user");
|
||||
// App we just launched should be the front and only element of both the
|
||||
// lists.
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_primary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_primary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Primary user launches a second mic-using app.
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user_another_app",
|
||||
"name_primary_user_another_app",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
// Primary user launches a second app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_primary_user_another_app", "name_primary_user_another_app",
|
||||
/*use_camera=*/true, /*use_microphone=*/true);
|
||||
|
||||
// The returned list should contain two application names ordered by most
|
||||
// recently launched.
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 2u);
|
||||
EXPECT_EQ(app_names[0], u"name_primary_user_another_app");
|
||||
EXPECT_EQ(app_names[1], u"name_primary_user");
|
||||
// The lists should contain two application names ordered by most recently
|
||||
// launched.
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_primary_user_another_app", u"name_primary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_primary_user_another_app", u"name_primary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Secondary user is now the primary user.
|
||||
SetActiveUserAccountId(/*is_primary=*/false);
|
||||
|
||||
// Secondary user launches a mic-using app.
|
||||
// Secondary user launches an app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should be the front and only element of the returned
|
||||
// list.
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 1u);
|
||||
EXPECT_EQ(app_names[0], u"name_secondary_user");
|
||||
// App we just launched should be the front and only element of both the
|
||||
// lists.
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>({u"name_secondary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Secondary user launches a second mic-using app.
|
||||
// Secondary user launches a second app using both sensors.
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_secondary_user_another_app", "name_secondary_user_another_app",
|
||||
/*use_camera=*/false, /*use_microphone=*/true);
|
||||
/*use_camera=*/true, /*use_microphone=*/true);
|
||||
|
||||
// The returned list should contain two application names ordered by most
|
||||
// recently launched.
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 2u);
|
||||
EXPECT_EQ(app_names[0], u"name_secondary_user_another_app");
|
||||
EXPECT_EQ(app_names[1], u"name_secondary_user");
|
||||
// The lists should contain two application names ordered by most recently
|
||||
// launched.
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_secondary_user_another_app", u"name_secondary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_secondary_user_another_app", u"name_secondary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
|
||||
// Switch back to the primary user.
|
||||
SetActiveUserAccountId(/*is_primary=*/true);
|
||||
|
||||
// Both of the apps we launced for the primary user should be in the list
|
||||
// Both of the apps we launced for the primary user should be in the lists
|
||||
// ordered by most recently launched.
|
||||
app_names = GetAppsAccessingMicrophone();
|
||||
EXPECT_EQ(app_names.size(), 2u);
|
||||
EXPECT_EQ(app_names[0], u"name_primary_user_another_app");
|
||||
EXPECT_EQ(app_names[1], u"name_primary_user");
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_primary_user_another_app", u"name_primary_user"}),
|
||||
GetAppsAccessingCamera());
|
||||
EXPECT_EQ(std::vector<std::u16string>(
|
||||
{u"name_primary_user_another_app", u"name_primary_user"}),
|
||||
GetAppsAccessingMicrophone());
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierParameterizedTest, GetShortNameFromAppId) {
|
||||
|
Reference in New Issue
Block a user