| // 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. |
| |
| #include "chrome/browser/ui/ash/app_access_notifier.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/public/cpp/ash_prefs.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/shell.h" |
| #include "ash/system/privacy/privacy_indicators_controller.h" |
| #include "ash/system/privacy/privacy_indicators_tray_item_view.h" |
| #include "ash/system/status_area_widget.h" |
| #include "ash/system/unified/unified_system_tray.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "components/account_id/account_id.h" |
| #include "components/prefs/testing_pref_service.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" |
| #include "components/services/app_service/public/cpp/app_registry_cache.h" |
| #include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "components/services/app_service/public/cpp/capability_access.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "components/user_manager/user.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/message_center/message_center.h" |
| |
| namespace { |
| |
| constexpr char kPrivacyIndicatorsAppTypeHistogramName[] = |
| "Ash.PrivacyIndicators.AppAccessUpdate.Type"; |
| constexpr char kPrivacyIndicatorsLaunchSettingsHistogramName[] = |
| "Ash.PrivacyIndicators.LaunchSettings"; |
| |
| // Check the visibility of privacy indicators and their camera/microphone icons |
| // in all displays. |
| void ExpectPrivacyIndicatorsVisible(bool visible) { |
| for (auto* root_window_controller : |
| ash::Shell::Get()->GetAllRootWindowControllers()) { |
| EXPECT_EQ(root_window_controller->GetStatusAreaWidget() |
| ->unified_system_tray() |
| ->privacy_indicators_view() |
| ->GetVisible(), |
| visible); |
| } |
| } |
| |
| void ExpectPrivacyIndicatorsCameraIconVisible(bool visible) { |
| for (auto* root_window_controller : |
| ash::Shell::Get()->GetAllRootWindowControllers()) { |
| EXPECT_EQ(root_window_controller->GetStatusAreaWidget() |
| ->unified_system_tray() |
| ->privacy_indicators_view() |
| ->camera_icon() |
| ->GetVisible(), |
| visible); |
| } |
| } |
| |
| void ExpectPrivacyIndicatorsMicrophoneIconVisible(bool visible) { |
| for (auto* root_window_controller : |
| ash::Shell::Get()->GetAllRootWindowControllers()) { |
| EXPECT_EQ(root_window_controller->GetStatusAreaWidget() |
| ->unified_system_tray() |
| ->privacy_indicators_view() |
| ->microphone_icon() |
| ->GetVisible(), |
| visible); |
| } |
| } |
| |
| } // namespace |
| |
| class TestAppAccessNotifier : public AppAccessNotifier { |
| public: |
| TestAppAccessNotifier() = default; |
| TestAppAccessNotifier(const TestAppAccessNotifier&) = delete; |
| TestAppAccessNotifier& operator=(const TestAppAccessNotifier&) = delete; |
| ~TestAppAccessNotifier() override = default; |
| |
| void SetFakeActiveUserAccountId(AccountId id) { |
| user_account_id_ = id; |
| CheckActiveUserChanged(); |
| } |
| |
| AccountId GetActiveUserAccountId() override { return user_account_id_; } |
| |
| private: |
| AccountId user_account_id_ = EmptyAccountId(); |
| }; |
| |
| class AppAccessNotifierBaseTest : public testing::Test { |
| public: |
| AppAccessNotifierBaseTest() |
| : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} |
| AppAccessNotifierBaseTest(const AppAccessNotifierBaseTest&) = delete; |
| AppAccessNotifierBaseTest& operator=(const AppAccessNotifierBaseTest&) = |
| delete; |
| ~AppAccessNotifierBaseTest() override = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| |
| // Setting ash prefs for testing multi-display. |
| ash::RegisterLocalStatePrefs(local_state_.registry(), /*for_test=*/true); |
| |
| ash::AshTestHelper::InitParams params; |
| params.local_state = &local_state_; |
| ash_test_helper_.SetUp(std::move(params)); |
| |
| auto fake_user_manager = std::make_unique<ash::FakeChromeUserManager>(); |
| fake_user_manager_ = fake_user_manager.get(); |
| scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>( |
| std::move(fake_user_manager)); |
| |
| app_access_notifier_ = std::make_unique<TestAppAccessNotifier>(); |
| |
| SetupPrimaryUser(); |
| } |
| |
| void TearDown() override { |
| app_access_notifier_.reset(); |
| ash_test_helper_.TearDown(); |
| } |
| |
| void SetupPrimaryUser() { |
| auto* primary_profile = testing_profile_manager_.CreateTestingProfile( |
| account_id_primary_user_.GetUserEmail()); |
| fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile( |
| account_id_primary_user_, false, user_manager::USER_TYPE_REGULAR, |
| primary_profile); |
| |
| registry_cache_primary_user_.SetAccountId(account_id_primary_user_); |
| apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache( |
| account_id_primary_user_, ®istry_cache_primary_user_); |
| capability_access_cache_primary_user_.SetAccountId( |
| account_id_primary_user_); |
| apps::AppCapabilityAccessCacheWrapper::Get().AddAppCapabilityAccessCache( |
| account_id_primary_user_, &capability_access_cache_primary_user_); |
| |
| SetActiveUserAccountId(/*is_primary=*/true); |
| } |
| |
| void SetupSecondaryUser() { |
| auto* secondary_profile = testing_profile_manager_.CreateTestingProfile( |
| account_id_secondary_user_.GetUserEmail()); |
| fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile( |
| account_id_secondary_user_, false, user_manager::USER_TYPE_REGULAR, |
| secondary_profile); |
| |
| registry_cache_secondary_user_.SetAccountId(account_id_secondary_user_); |
| apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache( |
| account_id_secondary_user_, ®istry_cache_secondary_user_); |
| capability_access_cache_secondary_user_.SetAccountId( |
| account_id_secondary_user_); |
| apps::AppCapabilityAccessCacheWrapper::Get().AddAppCapabilityAccessCache( |
| account_id_secondary_user_, &capability_access_cache_secondary_user_); |
| |
| 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_->GetAppsAccessingSensor( |
| ash::SensorDisabledNotificationDelegate::Sensor::kMicrophone); |
| } |
| |
| static apps::AppPtr MakeApp(const std::string app_id, |
| const char* name, |
| apps::AppType app_type) { |
| apps::AppPtr app = std::make_unique<apps::App>(app_type, app_id); |
| app->name = name; |
| app->short_name = name; |
| return app; |
| } |
| |
| static apps::CapabilityAccessPtr MakeCapabilityAccess( |
| const std::string app_id, |
| absl::optional<bool> camera, |
| absl::optional<bool> microphone) { |
| auto access = std::make_unique<apps::CapabilityAccess>(app_id); |
| access->camera = camera; |
| access->microphone = microphone; |
| return access; |
| } |
| |
| void LaunchAppUsingCameraOrMicrophone( |
| const std::string id, |
| const char* name, |
| bool use_camera, |
| bool use_microphone, |
| apps::AppType app_type = apps::AppType::kChromeApp) { |
| apps::AppRegistryCache* reg_cache = |
| app_access_notifier_->GetActiveUserAppRegistryCache(); |
| apps::AppCapabilityAccessCache* cap_cache = |
| app_access_notifier_->GetActiveUserAppCapabilityAccessCache(); |
| |
| std::vector<apps::AppPtr> registry_deltas; |
| registry_deltas.push_back(MakeApp(id, name, app_type)); |
| reg_cache->OnApps(std::move(registry_deltas), apps::AppType::kUnknown, |
| /*should_notify_initialized=*/false); |
| |
| std::vector<apps::CapabilityAccessPtr> capability_access_deltas; |
| capability_access_deltas.push_back( |
| MakeCapabilityAccess(id, use_camera, use_microphone)); |
| cap_cache->OnCapabilityAccesses(std::move(capability_access_deltas)); |
| } |
| |
| // Set the active account, whether to use the primary or secondary fake user |
| // account. |
| void SetActiveUserAccountId(bool is_primary) { |
| auto id = |
| is_primary ? account_id_primary_user_ : account_id_secondary_user_; |
| app_access_notifier_->SetFakeActiveUserAccountId(id); |
| |
| fake_user_manager_->LoginUser(id); |
| fake_user_manager_->SwitchActiveUser(id); |
| } |
| |
| const AccountId account_id_primary_user_ = |
| AccountId::FromUserEmail("primary_profile"); |
| const AccountId account_id_secondary_user_ = |
| AccountId::FromUserEmail("secondary_profile"); |
| |
| std::unique_ptr<TestAppAccessNotifier> app_access_notifier_; |
| |
| apps::AppRegistryCache registry_cache_primary_user_; |
| apps::AppCapabilityAccessCache capability_access_cache_primary_user_; |
| apps::AppRegistryCache registry_cache_secondary_user_; |
| apps::AppCapabilityAccessCache capability_access_cache_secondary_user_; |
| |
| ash::FakeChromeUserManager* fake_user_manager_ = nullptr; |
| std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_; |
| |
| // This instance is needed for setting up `ash_test_helper_`. |
| // See //docs/threading_and_tasks_testing.md. |
| content::BrowserTaskEnvironment task_environment_; |
| |
| TestingProfileManager testing_profile_manager_; |
| |
| // Use this for testing multi-display. |
| TestingPrefServiceSimple local_state_; |
| |
| ash::AshTestHelper ash_test_helper_; |
| }; |
| |
| class AppAccessNotifierParameterizedTest |
| : public AppAccessNotifierBaseTest, |
| public ::testing::WithParamInterface< |
| std::tuple</*IsPrivacyIndicatorsFeatureEnabled()=*/bool, |
| /*IsCrosPrivacyHubEnabled()=*/bool>> { |
| public: |
| AppAccessNotifierParameterizedTest() = default; |
| AppAccessNotifierParameterizedTest( |
| const AppAccessNotifierParameterizedTest&) = delete; |
| AppAccessNotifierParameterizedTest& operator=( |
| const AppAccessNotifierParameterizedTest&) = delete; |
| ~AppAccessNotifierParameterizedTest() override = default; |
| |
| // AppAccessNotifierBaseTest: |
| void SetUp() override { |
| std::vector<base::test::FeatureRef> disabled_features; |
| |
| if (!IsPrivacyIndicatorsFeatureEnabled()) |
| disabled_features.push_back(ash::features::kPrivacyIndicators); |
| |
| if (!IsCrosPrivacyHubEnabled()) |
| disabled_features.push_back(ash::features::kCrosPrivacyHub); |
| |
| scoped_feature_list_.InitWithFeatures({}, disabled_features); |
| |
| AppAccessNotifierBaseTest::SetUp(); |
| } |
| |
| bool IsPrivacyIndicatorsFeatureEnabled() const { |
| return std::get<0>(GetParam()); |
| } |
| |
| bool IsCrosPrivacyHubEnabled() const { return std::get<1>(GetParam()); } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| class AppAccessNotifierPrivacyIndicatorTest : public AppAccessNotifierBaseTest { |
| public: |
| AppAccessNotifierPrivacyIndicatorTest() = default; |
| AppAccessNotifierPrivacyIndicatorTest( |
| const AppAccessNotifierPrivacyIndicatorTest&) = delete; |
| AppAccessNotifierPrivacyIndicatorTest& operator=( |
| const AppAccessNotifierPrivacyIndicatorTest&) = delete; |
| ~AppAccessNotifierPrivacyIndicatorTest() override = default; |
| |
| // AppAccessNotifierBaseTest: |
| void SetUp() override { |
| scoped_feature_list_.InitWithFeatures({ash::features::kPrivacyIndicators}, |
| {}); |
| AppAccessNotifierBaseTest::SetUp(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| AppAccessNotifierParameterizedTest, |
| ::testing::Combine(/*IsPrivacyIndicatorsFeatureEnabled()=*/testing::Bool(), |
| /*IsCrosPrivacyHubEnabled()=*/testing::Bool())); |
| |
| TEST_P(AppAccessNotifierParameterizedTest, NoAppsLaunched) { |
| // 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, AppLaunchedNotUsingCameraAndMic) { |
| LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/false, |
| /*use_microphone=*/false); |
| |
| // 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, AppLaunchedUsingCameraAndMic) { |
| LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/true, |
| /*use_microphone=*/true); |
| |
| // 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, |
| MultipleAppsLaunchedUsingBothCameraAndMic) { |
| LaunchAppUsingCameraOrMicrophone("id_rose", "name_rose", /*use_camera=*/true, |
| /*use_microphone=*/true); |
| LaunchAppUsingCameraOrMicrophone("id_mars", "name_mars", /*use_camera=*/true, |
| /*use_microphone=*/true); |
| LaunchAppUsingCameraOrMicrophone("id_zara", "name_zara", /*use_camera=*/true, |
| /*use_microphone=*/true); |
| LaunchAppUsingCameraOrMicrophone( |
| "id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false); |
| |
| // 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 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=*/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 lists anymore. Zara should be at the front of |
| // both of the lists. |
| LaunchAppUsingCameraOrMicrophone( |
| "id_oscar", "name_oscar", /*use_camera=*/false, /*use_microphone=*/false); |
| 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) { |
| // Prepare the secondary user. |
| SetupSecondaryUser(); |
| |
| // Primary user is the active user. |
| SetActiveUserAccountId(/*is_primary=*/true); |
| |
| // Primary user launches an app using both sensors. |
| LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user", |
| /*use_camera=*/true, |
| /*use_microphone=*/true); |
| |
| // 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 an app using both sensors. |
| LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user", |
| /*use_camera=*/true, |
| /*use_microphone=*/true); |
| |
| // 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. |
| SetActiveUserAccountId(/*is_primary=*/true); |
| LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user", |
| /*use_camera=*/false, |
| /*use_microphone=*/false); |
| 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 in the lists. |
| SetActiveUserAccountId(/*is_primary=*/false); |
| 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); |
| EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingCamera()); |
| EXPECT_EQ(std::vector<std::u16string>(), GetAppsAccessingMicrophone()); |
| } |
| |
| TEST_P(AppAccessNotifierParameterizedTest, MultipleUsersMultipleApps) { |
| // Prepare the secondary user. |
| SetupSecondaryUser(); |
| |
| // Primary user is the active user. |
| SetActiveUserAccountId(/*is_primary=*/true); |
| |
| // Primary user launches an app using both sensors. |
| LaunchAppUsingCameraOrMicrophone("id_primary_user", "name_primary_user", |
| /*use_camera=*/true, |
| /*use_microphone=*/true); |
| |
| // 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 app using both sensors. |
| LaunchAppUsingCameraOrMicrophone( |
| "id_primary_user_another_app", "name_primary_user_another_app", |
| /*use_camera=*/true, /*use_microphone=*/true); |
| |
| // 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 an app using both sensors. |
| LaunchAppUsingCameraOrMicrophone("id_secondary_user", "name_secondary_user", |
| /*use_camera=*/true, |
| /*use_microphone=*/true); |
| |
| // 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 app using both sensors. |
| LaunchAppUsingCameraOrMicrophone( |
| "id_secondary_user_another_app", "name_secondary_user_another_app", |
| /*use_camera=*/true, /*use_microphone=*/true); |
| |
| // 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 lists |
| // 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()); |
| } |
| |
| TEST_P(AppAccessNotifierParameterizedTest, GetShortNameFromAppId) { |
| // Test that GetAppShortNameFromAppId works properly. |
| const std::string id = "test_app_id"; |
| LaunchAppUsingCameraOrMicrophone(id, "test_app_name", /*use_camera=*/false, |
| /*use_microphone=*/true); |
| EXPECT_EQ(AppAccessNotifier::GetAppShortNameFromAppId(id), u"test_app_name"); |
| } |
| |
| TEST_F(AppAccessNotifierPrivacyIndicatorTest, AppAccessNotification) { |
| // 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"; |
| const std::string notification_id1 = |
| ash::GetPrivacyIndicatorsNotificationId(id1); |
| const std::string notification_id2 = |
| ash::GetPrivacyIndicatorsNotificationId(id2); |
| |
| 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( |
| notification_id1)); |
| EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById( |
| notification_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( |
| notification_id1)); |
| EXPECT_FALSE(message_center::MessageCenter::Get()->FindNotificationById( |
| notification_id2)); |
| |
| LaunchAppUsingCameraOrMicrophone(id1, "test_app_name", /*use_camera=*/true, |
| /*use_microphone=*/true); |
| EXPECT_TRUE(message_center::MessageCenter::Get()->FindNotificationById( |
| notification_id1)); |
| } |
| |
| TEST_F(AppAccessNotifierPrivacyIndicatorTest, PrivacyIndicatorsVisibility) { |
| // Uses normal animation duration so that the icons would not be immediately |
| // hidden after the animation. |
| ui::ScopedAnimationDurationScaleMode animation_scale( |
| ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); |
| |
| // Make sure privacy indicators work on multiple displays. |
| display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager()) |
| .UpdateDisplay("800x800,801+0-800x800"); |
| |
| ExpectPrivacyIndicatorsVisible(/*visible=*/false); |
| |
| // Privacy indicators should show up if at least camera or microphone is being |
| // accessed. The icons should show up accordingly (only at the start of the |
| // animation). |
| LaunchAppUsingCameraOrMicrophone("test_app_id", "test_app_name", |
| /*use_camera=*/true, |
| /*use_microphone=*/true); |
| ExpectPrivacyIndicatorsVisible(/*visible=*/true); |
| ExpectPrivacyIndicatorsCameraIconVisible(/*visible=*/true); |
| ExpectPrivacyIndicatorsMicrophoneIconVisible(/*visible=*/true); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/false); |
| ExpectPrivacyIndicatorsVisible(/*visible=*/false); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id", "test_app_name", |
| /*use_camera=*/true, |
| /*use_microphone=*/false); |
| ExpectPrivacyIndicatorsVisible(/*visible=*/true); |
| ExpectPrivacyIndicatorsCameraIconVisible(/*visible=*/true); |
| ExpectPrivacyIndicatorsMicrophoneIconVisible(/*visible=*/false); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/true); |
| ExpectPrivacyIndicatorsVisible(/*visible=*/true); |
| ExpectPrivacyIndicatorsCameraIconVisible(/*visible=*/false); |
| ExpectPrivacyIndicatorsMicrophoneIconVisible(/*visible=*/true); |
| } |
| |
| TEST_F(AppAccessNotifierPrivacyIndicatorTest, RecordAppType) { |
| base::HistogramTester histograms; |
| LaunchAppUsingCameraOrMicrophone("test_app_id1", "test_app_name", |
| /*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*app_type=*/apps::AppType::kArc); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsAppTypeHistogramName, |
| apps::AppType::kArc, 1); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id2", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*app_type=*/apps::AppType::kChromeApp); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsAppTypeHistogramName, |
| apps::AppType::kChromeApp, 1); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id3", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/false, |
| /*app_type=*/apps::AppType::kChromeApp); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsAppTypeHistogramName, |
| apps::AppType::kChromeApp, 2); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id4", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*app_type=*/apps::AppType::kSystemWeb); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsAppTypeHistogramName, |
| apps::AppType::kSystemWeb, 1); |
| } |
| |
| TEST_F(AppAccessNotifierPrivacyIndicatorTest, RecordLaunchSettings) { |
| // Make sure histograms with app type is being recorded after launching |
| // settings. |
| base::HistogramTester histograms; |
| LaunchAppUsingCameraOrMicrophone("test_app_id1", "test_app_name", |
| /*use_camera=*/true, |
| /*use_microphone=*/false, |
| /*app_type=*/apps::AppType::kArc); |
| AppAccessNotifier::LaunchAppSettings("test_app_id1"); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsLaunchSettingsHistogramName, |
| apps::AppType::kArc, 1); |
| |
| LaunchAppUsingCameraOrMicrophone("test_app_id2", "test_app_name", |
| /*use_camera=*/false, |
| /*use_microphone=*/true, |
| /*app_type=*/apps::AppType::kChromeApp); |
| AppAccessNotifier::LaunchAppSettings("test_app_id2"); |
| histograms.ExpectBucketCount(kPrivacyIndicatorsLaunchSettingsHistogramName, |
| apps::AppType::kChromeApp, 1); |
| } |