PrivacyIndicators: Add notifications when camera or mic is in used.
This CL adds a controller that manages adding, updating, and removing the notification when camera/mic access changes. This controller will also be used in future CLs to modify the status area when privacy indicators are added there. This doesn't work on lacros yet. Will address it in later CLs. We also make AppAccessNotifierTest parameterized to test both when kPrivacyIndicators is enabled and disabled. Added a test case for managing camera/mic access notification. Design doc: go/cros-privacy-indicators-design Bug: 1344681 Change-Id: I7d2415d187ad41759dd0d9fde6f14e07ffd86a58 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3763634 Reviewed-by: James Cook <jamescook@chromium.org> Reviewed-by: Alex Newcomer <newcomer@chromium.org> Commit-Queue: Andre Le <leandre@chromium.org> Cr-Commit-Position: refs/heads/main@{#1025907}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
f9d3ff5436
commit
a1b75c1fdf
@ -1525,6 +1525,8 @@ component("ash") {
|
||||
"system/power/tray_power.h",
|
||||
"system/power/video_activity_notifier.cc",
|
||||
"system/power/video_activity_notifier.h",
|
||||
"system/privacy/privacy_indicators_controller.cc",
|
||||
"system/privacy/privacy_indicators_controller.h",
|
||||
"system/privacy_screen/privacy_screen_feature_pod_controller.cc",
|
||||
"system/privacy_screen/privacy_screen_feature_pod_controller.h",
|
||||
"system/privacy_screen/privacy_screen_toast_controller.cc",
|
||||
|
@ -3856,6 +3856,17 @@ Here are some things you can try to get started.
|
||||
An application is using your microphone
|
||||
</message>
|
||||
|
||||
<!-- For privacy indicators notification -->
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC" desc="Title for a notification shown when an app is using the camera and the microphone.">
|
||||
Camera and microphone in use
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA" desc="Title for a notification shown when an app is using the camera.">
|
||||
Camera in use
|
||||
</message>
|
||||
<message name="IDS_PRIVACY_NOTIFICATION_TITLE_MIC" desc="Title for a notification shown when an app is using the microphone.">
|
||||
Microphone in use
|
||||
</message>
|
||||
|
||||
<!-- Strings for microphone mute switch notification -->
|
||||
<message name="IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE" desc="Title for a notification shown to the users when an app tries to use the microphone while the microphone is muted.i Similar to IDS_MICROPHONE_MUTED_NOTIFICATION_TITLE_WITH_APP_NAME, except this message contains a generic app name string. Used when the name of the app that's using the microphone cannot be determined.">
|
||||
An app wants to use the microphone
|
||||
|
@ -0,0 +1 @@
|
||||
77edccf6a440d3b8cadbc2ec9646f65aa09a60c1
|
@ -0,0 +1 @@
|
||||
3dc59ae30e045f280ba2ce319cee0fd90fd2eedf
|
@ -0,0 +1 @@
|
||||
b6e5979c26ba5d0f5f8bb7168c00018b890a80f1
|
@ -158,7 +158,8 @@ enum class NotificationCatalogName {
|
||||
kLowPowerAdapter = 144,
|
||||
kTPMAutoUpdatePlanned = 145,
|
||||
kTPMAutoUpdateOnReboot = 146,
|
||||
kMaxValue = kTPMAutoUpdateOnReboot
|
||||
kPrivacyIndicators = 147,
|
||||
kMaxValue = kPrivacyIndicators
|
||||
};
|
||||
|
||||
// A living catalog that registers toasts.
|
||||
|
71
ash/system/privacy/privacy_indicators_controller.cc
Normal file
71
ash/system/privacy/privacy_indicators_controller.cc
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2022 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/privacy/privacy_indicators_controller.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ash/public/cpp/notification_utils.h"
|
||||
#include "ash/resources/vector_icons/vector_icons.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/message_center/message_center.h"
|
||||
#include "ui/message_center/public/cpp/notification.h"
|
||||
#include "ui/message_center/public/cpp/notification_types.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
const char kPrivacyIndicatorsNotificationIdPrefix[] = "privacy-indicators";
|
||||
const char kPrivacyIndicatorsNotifierId[] = "ash.privacy-indicators";
|
||||
} // namespace
|
||||
|
||||
void ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used) {
|
||||
auto* message_center = message_center::MessageCenter::Get();
|
||||
std::string id = kPrivacyIndicatorsNotificationIdPrefix + app_id;
|
||||
bool notification_exist = message_center->FindVisibleNotificationById(id);
|
||||
|
||||
if (!camera_is_used && !microphone_is_used) {
|
||||
if (notification_exist)
|
||||
message_center->RemoveNotification(id, /*by_user=*/false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::u16string title;
|
||||
if (camera_is_used && microphone_is_used) {
|
||||
title = l10n_util::GetStringUTF16(
|
||||
IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA_AND_MIC);
|
||||
} else if (camera_is_used) {
|
||||
title = l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_CAMERA);
|
||||
} else {
|
||||
title = l10n_util::GetStringUTF16(IDS_PRIVACY_NOTIFICATION_TITLE_MIC);
|
||||
}
|
||||
|
||||
message_center::RichNotificationData optional_fields;
|
||||
optional_fields.pinned = true;
|
||||
// Make the notification low priority so that it is silently added (no popup).
|
||||
optional_fields.priority = message_center::LOW_PRIORITY;
|
||||
|
||||
auto notification = CreateSystemNotification(
|
||||
message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
|
||||
std::u16string(),
|
||||
/*display_source=*/std::u16string(),
|
||||
/*origin_url=*/GURL(),
|
||||
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
|
||||
kPrivacyIndicatorsNotifierId,
|
||||
NotificationCatalogName::kPrivacyIndicators),
|
||||
optional_fields,
|
||||
/*delegate=*/nullptr, kImeMenuMicrophoneIcon,
|
||||
message_center::SystemNotificationWarningLevel::NORMAL);
|
||||
|
||||
if (notification_exist) {
|
||||
message_center->UpdateNotification(id, std::move(notification));
|
||||
return;
|
||||
}
|
||||
message_center->AddNotification(std::move(notification));
|
||||
}
|
||||
|
||||
} // namespace ash
|
22
ash/system/privacy/privacy_indicators_controller.h
Normal file
22
ash/system/privacy/privacy_indicators_controller.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_CONTROLLER_H_
|
||||
#define ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_CONTROLLER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// Add, update, or remove the privacy notification associated with the given
|
||||
// `app_id`.
|
||||
void ASH_EXPORT ModifyPrivacyIndicatorsNotification(const std::string& app_id,
|
||||
bool camera_is_used,
|
||||
bool microphone_is_used);
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_SYSTEM_PRIVACY_PRIVACY_INDICATORS_CONTROLLER_H_
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/system/privacy/privacy_indicators_controller.h"
|
||||
#include "base/check.h"
|
||||
#include "base/containers/cxx20_erase.h"
|
||||
#include "base/strings/string_util.h"
|
||||
@ -101,7 +103,16 @@ void AppAccessNotifier::OnCapabilityAccessUpdate(
|
||||
const apps::CapabilityAccessUpdate& update) {
|
||||
base::Erase(mic_using_app_ids[active_user_account_id_], update.AppId());
|
||||
|
||||
if (update.Microphone() == apps::mojom::OptionalBool::kTrue) {
|
||||
bool microphone_is_used =
|
||||
update.Microphone() == apps::mojom::OptionalBool::kTrue;
|
||||
bool camera_is_used = update.Camera() == apps::mojom::OptionalBool::kTrue;
|
||||
|
||||
if (ash::features::IsPrivacyIndicatorsEnabled()) {
|
||||
ash::ModifyPrivacyIndicatorsNotification(update.AppId(), camera_is_used,
|
||||
microphone_is_used);
|
||||
}
|
||||
|
||||
if (microphone_is_used) {
|
||||
mic_using_app_ids[active_user_account_id_].push_front(update.AppId());
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache_wrapper.h"
|
||||
@ -18,6 +20,9 @@
|
||||
#include "components/user_manager/fake_user_manager.h"
|
||||
#include "components/user_manager/scoped_user_manager.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/message_center/message_center.h"
|
||||
|
||||
const char kPrivacyIndicatorsNotificationIdPrefix[] = "privacy-indicators";
|
||||
|
||||
class TestAppAccessNotifier : public AppAccessNotifier {
|
||||
public:
|
||||
@ -37,7 +42,8 @@ class TestAppAccessNotifier : public AppAccessNotifier {
|
||||
AccountId user_account_id_ = EmptyAccountId();
|
||||
};
|
||||
|
||||
class AppAccessNotifierTest : public testing::Test {
|
||||
class AppAccessNotifierTest : public testing::Test,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
AppAccessNotifierTest() = default;
|
||||
AppAccessNotifierTest(const AppAccessNotifierTest&) = delete;
|
||||
@ -46,6 +52,10 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
|
||||
void SetUp() override {
|
||||
testing::Test::SetUp();
|
||||
message_center::MessageCenter::Initialize();
|
||||
|
||||
scoped_feature_list_.InitWithFeatureState(
|
||||
ash::features::kPrivacyIndicators, IsPrivacyIndicatorsFeatureEnabled());
|
||||
|
||||
auto fake_user_manager = std::make_unique<user_manager::FakeUserManager>();
|
||||
fake_user_manager_ = fake_user_manager.get();
|
||||
@ -60,9 +70,12 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
|
||||
void TearDown() override {
|
||||
microphone_mute_notification_delegate_.reset();
|
||||
message_center::MessageCenter::Shutdown();
|
||||
testing::Test::TearDown();
|
||||
}
|
||||
|
||||
bool IsPrivacyIndicatorsFeatureEnabled() const { return GetParam(); }
|
||||
|
||||
void SetupPrimaryUser() {
|
||||
registry_cache_primary_user_.SetAccountId(account_id_primary_user_);
|
||||
apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(
|
||||
@ -102,7 +115,7 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
cap_cache, reg_cache);
|
||||
}
|
||||
|
||||
static apps::AppPtr MakeApp(const char* app_id, const char* name) {
|
||||
static apps::AppPtr MakeApp(const std::string app_id, const char* name) {
|
||||
apps::AppPtr app =
|
||||
std::make_unique<apps::App>(apps::AppType::kChromeApp, app_id);
|
||||
app->name = name;
|
||||
@ -111,19 +124,21 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
}
|
||||
|
||||
static apps::mojom::CapabilityAccessPtr MakeCapabilityAccess(
|
||||
const char* app_id,
|
||||
const std::string app_id,
|
||||
apps::mojom::OptionalBool camera,
|
||||
apps::mojom::OptionalBool microphone) {
|
||||
apps::mojom::CapabilityAccessPtr access =
|
||||
apps::mojom::CapabilityAccess::New();
|
||||
access->app_id = app_id;
|
||||
access->camera = apps::mojom::OptionalBool::kFalse;
|
||||
access->camera = camera;
|
||||
access->microphone = microphone;
|
||||
return access;
|
||||
}
|
||||
|
||||
void LaunchAppUsingMicrophone(const char* id,
|
||||
const char* name,
|
||||
bool use_microphone) {
|
||||
void LaunchAppUsingCameraOrMicrophone(const std::string id,
|
||||
const char* name,
|
||||
bool use_camera,
|
||||
bool use_microphone) {
|
||||
bool is_primary_user =
|
||||
(microphone_mute_notification_delegate_->GetActiveUserAccountId() ==
|
||||
account_id_primary_user_);
|
||||
@ -149,8 +164,11 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
|
||||
std::vector<apps::mojom::CapabilityAccessPtr> capability_access_deltas;
|
||||
capability_access_deltas.push_back(MakeCapabilityAccess(
|
||||
id, use_microphone ? apps::mojom::OptionalBool::kTrue
|
||||
: apps::mojom::OptionalBool::kFalse));
|
||||
id,
|
||||
use_camera ? apps::mojom::OptionalBool::kTrue
|
||||
: apps::mojom::OptionalBool::kFalse,
|
||||
use_microphone ? apps::mojom::OptionalBool::kTrue
|
||||
: apps::mojom::OptionalBool::kFalse));
|
||||
cap_cache->OnCapabilityAccesses(std::move(capability_access_deltas));
|
||||
}
|
||||
|
||||
@ -174,24 +192,33 @@ class AppAccessNotifierTest : public testing::Test {
|
||||
|
||||
user_manager::FakeUserManager* fake_user_manager_ = nullptr;
|
||||
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
|
||||
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
TEST_F(AppAccessNotifierTest, NoAppsLaunched) {
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
All,
|
||||
AppAccessNotifierTest,
|
||||
/*IsPrivacyIndicatorsFeatureEnabled()=*/::testing::Bool());
|
||||
|
||||
TEST_P(AppAccessNotifierTest, NoAppsLaunched) {
|
||||
// Should return a completely value-free app_name.
|
||||
absl::optional<std::u16string> app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_FALSE(app_name.has_value());
|
||||
}
|
||||
|
||||
TEST_F(AppAccessNotifierTest, AppLaunchedNotUsingMicrophone) {
|
||||
LaunchAppUsingMicrophone("id_rose", "name_rose", false);
|
||||
TEST_P(AppAccessNotifierTest, AppLaunchedNotUsingMicrophone) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
|
||||
// Should return a completely value-free app_name.
|
||||
absl::optional<std::u16string> app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_FALSE(app_name.has_value());
|
||||
}
|
||||
|
||||
TEST_F(AppAccessNotifierTest, AppLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingMicrophone("id_rose", "name_rose", true);
|
||||
TEST_P(AppAccessNotifierTest, AppLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// Should return the name of our app.
|
||||
absl::optional<std::u16string> app_name = GetAppAccessingMicrophone();
|
||||
@ -199,11 +226,15 @@ TEST_F(AppAccessNotifierTest, AppLaunchedUsingMicrophone) {
|
||||
EXPECT_EQ(app_name, u"name_rose");
|
||||
}
|
||||
|
||||
TEST_F(AppAccessNotifierTest, MultipleAppsLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingMicrophone("id_rose", "name_rose", true);
|
||||
LaunchAppUsingMicrophone("id_mars", "name_mars", true);
|
||||
LaunchAppUsingMicrophone("id_zara", "name_zara", true);
|
||||
LaunchAppUsingMicrophone("id_oscar", "name_oscar", false);
|
||||
TEST_P(AppAccessNotifierTest, MultipleAppsLaunchedUsingMicrophone) {
|
||||
LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_mars", "name_mars", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_zara", "name_zara", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false);
|
||||
|
||||
// Most recently launched mic-using app should be the one we use for the
|
||||
// notification.
|
||||
@ -212,20 +243,22 @@ TEST_F(AppAccessNotifierTest, MultipleAppsLaunchedUsingMicrophone) {
|
||||
EXPECT_EQ(app_name, u"name_zara");
|
||||
|
||||
// Oscar starts using the mic, Oscar shows up in the notification.
|
||||
LaunchAppUsingMicrophone("id_oscar", "name_oscar", true);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/true);
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_TRUE(app_name.has_value());
|
||||
EXPECT_EQ(app_name, u"name_oscar");
|
||||
|
||||
// If we "kill" Oscar (set to no longer be using the mic or camera),
|
||||
// the notification shows Zara again.
|
||||
LaunchAppUsingMicrophone("id_oscar", "name_oscar", false);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false);
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_TRUE(app_name.has_value());
|
||||
EXPECT_EQ(app_name, u"name_zara");
|
||||
}
|
||||
|
||||
TEST_F(AppAccessNotifierTest, MultipleUsers) {
|
||||
TEST_P(AppAccessNotifierTest, MultipleUsers) {
|
||||
// Prepare the secondary user.
|
||||
SetupSecondaryUser();
|
||||
|
||||
@ -233,7 +266,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsers) {
|
||||
SetActiveUserAccountId(account_id_primary_user_);
|
||||
|
||||
// Primary user launches a mic-using app.
|
||||
LaunchAppUsingMicrophone("id_primary_user", "name_primary_user", true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
absl::optional<std::u16string> app_name = GetAppAccessingMicrophone();
|
||||
@ -244,7 +279,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsers) {
|
||||
SetActiveUserAccountId(account_id_secondary_user_);
|
||||
|
||||
// Secondary user launches a mic-using app.
|
||||
LaunchAppUsingMicrophone("id_secondary_user", "name_secondary_user", true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
@ -254,7 +291,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsers) {
|
||||
// Switch back to the primary user and "kill" the app it was running, no app
|
||||
// name to show.
|
||||
SetActiveUserAccountId(account_id_primary_user_);
|
||||
LaunchAppUsingMicrophone("id_primary_user", "name_primary_user", false);
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_FALSE(app_name.has_value());
|
||||
|
||||
@ -267,12 +306,14 @@ TEST_F(AppAccessNotifierTest, MultipleUsers) {
|
||||
|
||||
// Now "kill" our secondary user's app and verify that there's no name to
|
||||
// show.
|
||||
LaunchAppUsingMicrophone("id_secondary_user", "name_secondary_user", false);
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
EXPECT_FALSE(app_name.has_value());
|
||||
}
|
||||
|
||||
TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
TEST_P(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
// Prepare the secondary user.
|
||||
SetupSecondaryUser();
|
||||
|
||||
@ -280,7 +321,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
SetActiveUserAccountId(account_id_primary_user_);
|
||||
|
||||
// Primary user launches a mic-using app.
|
||||
LaunchAppUsingMicrophone("id_primary_user", "name_primary_user", true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
absl::optional<std::u16string> app_name = GetAppAccessingMicrophone();
|
||||
@ -288,8 +331,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
EXPECT_EQ(app_name, u"name_primary_user");
|
||||
|
||||
// Primary user launches a second mic-using app.
|
||||
LaunchAppUsingMicrophone("id_primary_user", "name_primary_user_another_app",
|
||||
true);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_primary_user", "name_primary_user_another_app", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
@ -300,7 +344,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
SetActiveUserAccountId(account_id_secondary_user_);
|
||||
|
||||
// Secondary user launches a mic-using app.
|
||||
LaunchAppUsingMicrophone("id_secondary_user", "name_secondary_user", true);
|
||||
LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user",
|
||||
/*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
@ -308,8 +354,9 @@ TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
EXPECT_EQ(app_name, u"name_secondary_user");
|
||||
|
||||
// Secondary user launches a second mic-using app.
|
||||
LaunchAppUsingMicrophone("id_secondary_user",
|
||||
"name_secondary_user_another_app", true);
|
||||
LaunchAppUsingCameraOrMicrophone(
|
||||
"id_secondary_user", "name_secondary_user_another_app",
|
||||
/*use_camera=*/false, /*use_microphone=*/true);
|
||||
|
||||
// App we just launched should show up in the notification.
|
||||
app_name = GetAppAccessingMicrophone();
|
||||
@ -324,3 +371,36 @@ TEST_F(AppAccessNotifierTest, MultipleUsersMultipleApps) {
|
||||
EXPECT_TRUE(app_name.has_value());
|
||||
EXPECT_EQ(app_name, u"name_primary_user_another_app");
|
||||
}
|
||||
|
||||
TEST_P(AppAccessNotifierTest, AppAccessNotification) {
|
||||
if (!IsPrivacyIndicatorsFeatureEnabled())
|
||||
return;
|
||||
|
||||
// Test that notifications get created/removed when an app is accessing camera
|
||||
// or microphone.
|
||||
const std::string id1 = "test_app_id_1";
|
||||
const std::string id2 = "test_app_id_2";
|
||||
|
||||
LaunchAppUsingCameraOrMicrophone(id1, "test_app_name", /*use_camera=*/false,
|
||||
/*use_microphone=*/true);
|
||||
LaunchAppUsingCameraOrMicrophone(id2, "test_app_name", /*use_camera=*/true,
|
||||
/*use_microphone=*/false);
|
||||
EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id1));
|
||||
EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id2));
|
||||
|
||||
LaunchAppUsingCameraOrMicrophone(id1, "test_app_name", /*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
LaunchAppUsingCameraOrMicrophone(id2, "test_app_name", /*use_camera=*/false,
|
||||
/*use_microphone=*/false);
|
||||
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id1));
|
||||
EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id2));
|
||||
|
||||
LaunchAppUsingCameraOrMicrophone(id1, "test_app_name", /*use_camera=*/true,
|
||||
/*use_microphone=*/true);
|
||||
EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById(
|
||||
kPrivacyIndicatorsNotificationIdPrefix + id1));
|
||||
}
|
||||
|
@ -7167,7 +7167,7 @@ test("unit_tests") {
|
||||
"../browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc",
|
||||
"../browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc",
|
||||
"../browser/ui/ash/ambient/ambient_client_impl_unittest.cc",
|
||||
"../browser/ui/ash/app_access_notifier.cc",
|
||||
"../browser/ui/ash/app_access_notifier_unittest.cc",
|
||||
"../browser/ui/ash/app_icon_color_cache_unittest.cc",
|
||||
"../browser/ui/ash/assistant/assistant_state_client_unittest.cc",
|
||||
"../browser/ui/ash/assistant/device_actions_unittest.cc",
|
||||
|
Reference in New Issue
Block a user