0

scanner: Add coarse-grained CanShowUi metrics

These metrics will be useful for debugging feature access issues, and
getting an overview of how many users have the Scanner UI shown.

Bug: b:397105844
Change-Id: I6a6a2100b3665daa3b5c2d5d056589840ba56327
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6275365
Reviewed-by: Michelle Chen <michellegc@google.com>
Reviewed-by: Tony Yeoman <tby@chromium.org>
Commit-Queue: Michael Cui <mlcui@google.com>
Cr-Commit-Position: refs/heads/main@{#1421682}
This commit is contained in:
Michael Cui
2025-02-18 15:44:25 -08:00
committed by Chromium LUCI CQ
parent 0c0498b7cc
commit b86adced66
5 changed files with 160 additions and 1 deletions

@ -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;
}

@ -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();

@ -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)

@ -51,6 +51,9 @@ INSTANTIATE_TEST_SUITE_P(
kNewGoogleSheetPopulatedActionExecutionFailed,
kNewGoogleDocPopulatedActionExecutionFailed,
kCopyToClipboardPopulatedActionExecutionFailed,
kCanShowUiReturnedFalse,
kCanShowUiReturnedTrueWithoutConsent,
kCanShowUiReturnedTrueWithConsent,
}));
TEST_P(ScannerMetricsTest, Record) {

@ -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 &quot;do not show UI&quot;"/>
<int value="28"
label="UI access checks resulted in &quot;show UI, but user not
consented&quot;"/>
<int value="29"
label="UI access checks resulted in &quot;show UI and user
consented&quot;"/>
</enum>
<!-- LINT.ThenChange(//ash/scanner/scanner_metrics.h:ScannerFeatureUserState) -->