diff --git a/ash/scanner/scanner_controller.cc b/ash/scanner/scanner_controller.cc index d8da75c9404bc..ce71b581af57f 100644 --- a/ash/scanner/scanner_controller.cc +++ b/ash/scanner/scanner_controller.cc @@ -401,11 +401,15 @@ void ScannerController::RegisterProfilePrefs(PrefRegistrySimple* registry) { // static bool ScannerController::CanShowUiForShell() { if (!Shell::HasInstance()) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedFalse); return false; } ScannerController* controller = Shell::Get()->scanner_controller(); if (!controller) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedFalse); return false; } @@ -423,15 +427,22 @@ bool ScannerController::CanShowUi() { delegate_->GetProfileScopedDelegate(); if (profile_scoped_delegate == nullptr) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedFalse); return false; } specialized_features::FeatureAccessFailureSet checks = profile_scoped_delegate->CheckFeatureAccess(); + bool consent_not_accepted = checks.Has( + specialized_features::FeatureAccessFailure::kConsentNotAccepted); + checks.Remove( specialized_features::FeatureAccessFailure::kConsentNotAccepted); if (!checks.empty()) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedFalse); return false; } @@ -444,9 +455,18 @@ bool ScannerController::CanShowUi() { if (prefs != nullptr && prefs->GetInteger(prefs::kScannerEnterprisePolicyAllowed) == static_cast<int>(ScannerEnterprisePolicy::kDisallowed)) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedFalse); return false; } + if (consent_not_accepted) { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedTrueWithoutConsent); + } else { + RecordScannerFeatureUserState( + ScannerFeatureUserState::kCanShowUiReturnedTrueWithConsent); + } return true; } diff --git a/ash/scanner/scanner_controller_unittest.cc b/ash/scanner/scanner_controller_unittest.cc index 96253afafd9c3..1843489656d80 100644 --- a/ash/scanner/scanner_controller_unittest.cc +++ b/ash/scanner/scanner_controller_unittest.cc @@ -24,6 +24,7 @@ #include "ash/scanner/fake_scanner_profile_scoped_delegate.h" #include "ash/scanner/scanner_action_view_model.h" #include "ash/scanner/scanner_enterprise_policy.h" +#include "ash/scanner/scanner_metrics.h" #include "ash/scanner/scanner_session.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" @@ -38,6 +39,7 @@ #include "base/strings/string_split.h" #include "base/test/gmock_callback_support.h" #include "base/test/gmock_expected_support.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/protobuf_matchers.h" #include "base/test/scoped_feature_list.h" @@ -140,6 +142,20 @@ class MockToastManager : public ToastManager { MOCK_METHOD(void, Resume, (), (override)); }; +class MockScannerDelegate : public ScannerDelegate { + public: + MOCK_METHOD(ScannerProfileScopedDelegate*, + GetProfileScopedDelegate, + (), + (override)); + MOCK_METHOD(void, + OpenFeedbackDialog, + (const AccountId& account_id, + ScannerFeedbackInfo feedback_info, + SendFeedbackCallback send_feedback_callback), + (override)); +}; + class ScannerControllerTest : public AshTestBase { public: ScannerControllerTest() = default; @@ -356,6 +372,113 @@ TEST_F(ScannerControllerDisabledTest, CanShowUiForShellFalseWhenNoController) { EXPECT_FALSE(ScannerController::CanShowUiForShell()); } +TEST(ScannerControllerNoFixtureTest, CanShowUiForShellFalseWhenNoShellMetrics) { + base::HistogramTester histogram_tester; + + ASSERT_FALSE(Shell::HasInstance()); + ASSERT_FALSE(ScannerController::CanShowUiForShell()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedFalse, 1); +} + +TEST_F(ScannerControllerDisabledTest, + CanShowUiForShellFalseWhenNoControllerMetrics) { + base::HistogramTester histogram_tester; + + ASSERT_FALSE(Shell::Get()->scanner_controller()); + ASSERT_FALSE(ScannerController::CanShowUiForShell()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedFalse, 1); +} + +TEST(ScannerControllerNoFixtureTest, + CanShowUiFalseWhenNoProfileScopedDelegateMetrics) { + base::HistogramTester histogram_tester; + SessionControllerImpl session_controller; + auto mock_delegate = std::make_unique<MockScannerDelegate>(); + EXPECT_CALL(*mock_delegate, GetProfileScopedDelegate()) + .WillRepeatedly(Return(nullptr)); + ScannerController scanner_controller(std::move(mock_delegate), + session_controller); + + ASSERT_FALSE(scanner_controller.CanShowUi()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedFalse, 1); +} + +TEST_F(ScannerControllerTest, + CanShowUiFalseWhenFeatureAccessCheckFailsMetrics) { + base::HistogramTester histogram_tester; + ScannerController* scanner_controller = Shell::Get()->scanner_controller(); + ASSERT_TRUE(scanner_controller); + EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller), + CheckFeatureAccess) + .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{ + specialized_features::FeatureAccessFailure::kDisabledInSettings})); + + ASSERT_FALSE(scanner_controller->CanShowUi()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedFalse, 1); +} + +TEST_F(ScannerControllerTest, + CanShowUiFalseWhenEnterprisePolicyDisallowedMetrics) { + base::HistogramTester histogram_tester; + ScannerController* scanner_controller = Shell::Get()->scanner_controller(); + ASSERT_TRUE(scanner_controller); + EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller), + CheckFeatureAccess) + .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{})); + Shell::Get()->session_controller()->GetActivePrefService()->SetInteger( + prefs::kScannerEnterprisePolicyAllowed, + static_cast<int>(ScannerEnterprisePolicy::kDisallowed)); + + ASSERT_FALSE(scanner_controller->CanShowUi()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedFalse, 1); +} + +TEST_F(ScannerControllerTest, CanShowUiTrueWithoutConsentMetrics) { + base::HistogramTester histogram_tester; + ScannerController* scanner_controller = Shell::Get()->scanner_controller(); + ASSERT_TRUE(scanner_controller); + EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller), + CheckFeatureAccess) + .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{ + specialized_features::FeatureAccessFailure::kConsentNotAccepted})); + + ASSERT_TRUE(scanner_controller->CanShowUi()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedTrueWithoutConsent, 1); +} + +TEST_F(ScannerControllerTest, CanShowUiTrueWithConsentMetrics) { + base::HistogramTester histogram_tester; + ScannerController* scanner_controller = Shell::Get()->scanner_controller(); + ASSERT_TRUE(scanner_controller); + EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller), + CheckFeatureAccess) + .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{})); + + ASSERT_TRUE(scanner_controller->CanShowUi()); + + histogram_tester.ExpectBucketCount( + "Ash.ScannerFeature.UserState", + ScannerFeatureUserState::kCanShowUiReturnedTrueWithConsent, 1); +} + TEST_F(ScannerControllerTest, CannotShowConsentScreenEntryPointsIfOtherCheckFail) { ScannerController* scanner_controller = Shell::Get()->scanner_controller(); diff --git a/ash/scanner/scanner_metrics.h b/ash/scanner/scanner_metrics.h index 42c522cb0d1af..997e913ae5b2e 100644 --- a/ash/scanner/scanner_metrics.h +++ b/ash/scanner/scanner_metrics.h @@ -84,7 +84,12 @@ enum class ScannerFeatureUserState { kNewGoogleSheetPopulatedActionExecutionFailed, kNewGoogleDocPopulatedActionExecutionFailed, kCopyToClipboardPopulatedActionExecutionFailed, - kMaxValue = kCopyToClipboardPopulatedActionExecutionFailed, + + kCanShowUiReturnedFalse = 27, + kCanShowUiReturnedTrueWithoutConsent = 28, + kCanShowUiReturnedTrueWithConsent = 29, + + kMaxValue = kCanShowUiReturnedTrueWithConsent, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/ash/enums.xml:ScannerFeatureUserState) diff --git a/ash/scanner/scanner_metrics_unittest.cc b/ash/scanner/scanner_metrics_unittest.cc index 171ef429b7b35..26532c6e97936 100644 --- a/ash/scanner/scanner_metrics_unittest.cc +++ b/ash/scanner/scanner_metrics_unittest.cc @@ -51,6 +51,9 @@ INSTANTIATE_TEST_SUITE_P( kNewGoogleSheetPopulatedActionExecutionFailed, kNewGoogleDocPopulatedActionExecutionFailed, kCopyToClipboardPopulatedActionExecutionFailed, + kCanShowUiReturnedFalse, + kCanShowUiReturnedTrueWithoutConsent, + kCanShowUiReturnedTrueWithConsent, })); TEST_P(ScannerMetricsTest, Record) { diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml index 5a30d9f256282..882e55a511b50 100644 --- a/tools/metrics/histograms/metadata/ash/enums.xml +++ b/tools/metrics/histograms/metadata/ash/enums.xml @@ -2031,6 +2031,14 @@ chromeos/ash/components/peripheral_notification/peripheral_notification_manager. <int value="25" label="New Google Doc populated action failed on execution"/> <int value="26" label="Copy to clipboard populated action failed on execution"/> + <int value="27" + label="UI access checks resulted in "do not show UI""/> + <int value="28" + label="UI access checks resulted in "show UI, but user not + consented""/> + <int value="29" + label="UI access checks resulted in "show UI and user + consented""/> </enum> <!-- LINT.ThenChange(//ash/scanner/scanner_metrics.h:ScannerFeatureUserState) -->