Commit 0f06a24f authored by Saurabh Nijhara's avatar Saurabh Nijhara Committed by Commit Bot

Add CRX install error detail event for force installed extensions

We can get different error codes if the extension installation fails
due to crx install error.
We want to add crx install error detail event to the existing events for
the event based reporting for force installed extensions so that these
error codes can be recorded.

Bug: 1048640
Change-Id: I9a984d960c540087e8c211515c944ade444e8374
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2469556
Commit-Queue: Saurabh Nijhara <snijhara@google.com>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarSwapnil Gupta <swapnilgupta@google.com>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822608}
parent 60ce32c4
......@@ -379,6 +379,73 @@ ConvertUnpackerFailureReasonToProto(
}
}
// Helper function to convert extensions::CrxInstallErrorDetail to the
// ExtensionInstallReportLogEvent::CrxInstallErrorDetail proto.
em::ExtensionInstallReportLogEvent_CrxInstallErrorDetail
ConvertCrxInstallErrorDetailToProto(
extensions::CrxInstallErrorDetail error_detail) {
using Error = extensions::CrxInstallErrorDetail;
switch (error_detail) {
case Error::NONE:
return em::ExtensionInstallReportLogEvent::
CRX_INSTALL_ERROR_DETAIL_UNKNOWN;
case Error::CONVERT_USER_SCRIPT_TO_EXTENSION_FAILED:
return em::ExtensionInstallReportLogEvent::
CONVERT_USER_SCRIPT_TO_EXTENSION_FAILED;
case Error::UNEXPECTED_ID:
return em::ExtensionInstallReportLogEvent::UNEXPECTED_ID;
case Error::UNEXPECTED_VERSION:
return em::ExtensionInstallReportLogEvent::UNEXPECTED_VERSION;
case Error::MISMATCHED_VERSION:
return em::ExtensionInstallReportLogEvent::MISMATCHED_VERSION;
case Error::MANIFEST_INVALID:
return em::ExtensionInstallReportLogEvent::CRX_ERROR_MANIFEST_INVALID;
case Error::INSTALL_NOT_ENABLED:
return em::ExtensionInstallReportLogEvent::INSTALL_NOT_ENABLED;
case Error::OFFSTORE_INSTALL_DISALLOWED:
return em::ExtensionInstallReportLogEvent::OFFSTORE_INSTALL_DISALLOWED;
case Error::INCORRECT_APP_CONTENT_TYPE:
return em::ExtensionInstallReportLogEvent::INCORRECT_APP_CONTENT_TYPE;
case Error::NOT_INSTALLED_FROM_GALLERY:
return em::ExtensionInstallReportLogEvent::NOT_INSTALLED_FROM_GALLERY;
case Error::INCORRECT_INSTALL_HOST:
return em::ExtensionInstallReportLogEvent::INCORRECT_INSTALL_HOST;
case Error::DEPENDENCY_NOT_SHARED_MODULE:
return em::ExtensionInstallReportLogEvent::DEPENDENCY_NOT_SHARED_MODULE;
case Error::DEPENDENCY_OLD_VERSION:
return em::ExtensionInstallReportLogEvent::DEPENDENCY_OLD_VERSION;
case Error::DEPENDENCY_NOT_ALLOWLISTED:
return em::ExtensionInstallReportLogEvent::DEPENDENCY_NOT_ALLOWLISTED;
case Error::UNSUPPORTED_REQUIREMENTS:
return em::ExtensionInstallReportLogEvent::UNSUPPORTED_REQUIREMENTS;
case Error::EXTENSION_IS_BLOCKLISTED:
return em::ExtensionInstallReportLogEvent::EXTENSION_IS_BLOCKLISTED;
case Error::DISALLOWED_BY_POLICY:
return em::ExtensionInstallReportLogEvent::DISALLOWED_BY_POLICY;
case Error::KIOSK_MODE_ONLY:
return em::ExtensionInstallReportLogEvent::KIOSK_MODE_ONLY;
case Error::OVERLAPPING_WEB_EXTENT:
return em::ExtensionInstallReportLogEvent::OVERLAPPING_WEB_EXTENT;
case Error::CANT_DOWNGRADE_VERSION:
return em::ExtensionInstallReportLogEvent::CANT_DOWNGRADE_VERSION;
case Error::MOVE_DIRECTORY_TO_PROFILE_FAILED:
return em::ExtensionInstallReportLogEvent::
MOVE_DIRECTORY_TO_PROFILE_FAILED;
case Error::CANT_LOAD_EXTENSION:
return em::ExtensionInstallReportLogEvent::CANT_LOAD_EXTENSION;
case Error::USER_CANCELED:
return em::ExtensionInstallReportLogEvent::USER_CANCELED;
case Error::USER_ABORTED:
return em::ExtensionInstallReportLogEvent::USER_ABORTED;
case Error::UPDATE_NON_EXISTING_EXTENSION:
return em::ExtensionInstallReportLogEvent::UPDATE_NON_EXISTING_EXTENSION;
default:
NOTREACHED();
return em::ExtensionInstallReportLogEvent::
CRX_INSTALL_ERROR_DETAIL_UNKNOWN;
}
}
// Helper function to convert extensions::ManifestInvalidError to the
// ExtensionInstallReportLogEvent::ManifestInvalidError proto.
em::ExtensionInstallReportLogEvent_ManifestInvalidError
......@@ -508,6 +575,8 @@ void ExtensionInstallEventLogCollector::OnExtensionInstallationFailed(
extensions::InstallStageTracker::Get(profile_);
extensions::InstallStageTracker::InstallationData data =
install_stage_tracker->Get(extension_id);
// Extension type is only reported if extension installation failed after the
// unpacking stage.
if (data.extension_type) {
event->set_extension_type(enterprise_reporting::ConvertExtensionTypeToProto(
data.extension_type.value()));
......@@ -522,6 +591,13 @@ void ExtensionInstallEventLogCollector::OnExtensionInstallationFailed(
event->set_manifest_invalid_error(ConvertManifestInvalidErrorToProto(
data.manifest_invalid_error.value()));
}
// Crx install error detail is only reported if extension installation failed
// after the unpacking stage.
if (data.install_error_detail) {
event->set_crx_install_error_detail(
ConvertCrxInstallErrorDetailToProto(data.install_error_detail.value()));
}
extensions::ForceInstalledTracker* force_installed_tracker =
extensions::ExtensionSystem::Get(profile_)
->extension_service()
......
......@@ -233,9 +233,8 @@ class ExtensionInstallEventLogCollectorTest : public testing::Test {
// session. In this case no event is generated. This happens for example when
// all extensions are installed in context of the same user session.
TEST_F(ExtensionInstallEventLogCollectorTest, NoEventsByDefault) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
collector.reset();
EXPECT_EQ(0, delegate()->add_count());
......@@ -255,9 +254,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, LoginLogout) {
fake_user_manager->UserLoggedIn(account_id, user->username_hash(),
false /* browser_restart */,
false /* is_child */);
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
EXPECT_EQ(0, delegate()->add_for_all_count());
......@@ -326,9 +324,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, LoginTypes) {
}
TEST_F(ExtensionInstallEventLogCollectorTest, SuspendResume) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
power_manager::SuspendImminent_Reason_OTHER);
......@@ -373,9 +370,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, ConnectivityChanges) {
false /* browser_restart */,
false /* is_child */);
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
EXPECT_EQ(0, delegate()->add_for_all_count());
......@@ -420,9 +416,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, ConnectivityChanges) {
TEST_F(ExtensionInstallEventLogCollectorTest,
ExtensionInstallFailedWithoutMisconfiguration) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
// One extension failed.
install_stage_tracker()->ReportInfoOnNoUpdatesFailure(kExtensionId1,
......@@ -441,9 +436,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest,
TEST_F(ExtensionInstallEventLogCollectorTest,
ExtensionInstallFailedWithMisconfiguration) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
// One extension failed.
install_stage_tracker()->ReportInfoOnNoUpdatesFailure(kExtensionId1, "");
......@@ -459,29 +453,58 @@ TEST_F(ExtensionInstallEventLogCollectorTest,
EXPECT_TRUE(delegate()->last_request().event.is_misconfiguration_failure());
}
// Simulate failure after unpacking, so extension type should be reported.
TEST_F(ExtensionInstallEventLogCollectorTest, ExtensionInstallFailedWithType) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
// Verifies that a new event is created when the extension installation fails
// due to invalid ID supplied in the force list policy.
TEST_F(ExtensionInstallEventLogCollectorTest, ExtensionInstallFailed) {
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
extensions::InstallStageTracker* tracker =
extensions::InstallStageTracker::Get(profile());
// One extension failed.
tracker->ReportFailure(
kExtensionId1,
extensions::InstallStageTracker::FailureReason::INVALID_ID);
ASSERT_TRUE(VerifyEventAddedSuccessfully(1 /*expected_add_count*/,
0 /*expected_add_all_count*/));
EXPECT_EQ(em::ExtensionInstallReportLogEvent::INSTALLATION_FAILED,
delegate()->last_request().event.event_type());
EXPECT_EQ(em::ExtensionInstallReportLogEvent::INVALID_ID,
delegate()->last_request().event.failure_reason());
EXPECT_FALSE(delegate()->last_request().event.is_misconfiguration_failure());
}
// Verifies that a new event is created when the extension installation fails
// due to crx install error. as failure occurs due during crx installation, crx
// install error and event type should be reported.
TEST_F(ExtensionInstallEventLogCollectorTest,
ExtensionInstallFailedWithCrxError) {
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
extensions::InstallStageTracker* tracker =
extensions::InstallStageTracker::Get(profile());
// One extension failed.
tracker->ReportExtensionType(kExtensionId1,
extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
tracker->ReportFailure(kExtensionId1,
extensions::InstallStageTracker::FailureReason::
CRX_INSTALL_ERROR_DECLINED);
tracker->ReportCrxInstallError(
kExtensionId1,
extensions::InstallStageTracker::FailureReason::
CRX_INSTALL_ERROR_DECLINED,
extensions::CrxInstallErrorDetail::KIOSK_MODE_ONLY);
ASSERT_TRUE(VerifyEventAddedSuccessfully(1 /*expected_add_count*/,
0 /*expected_add_all_count*/));
EXPECT_EQ(em::ExtensionInstallReportLogEvent::INSTALLATION_FAILED,
delegate()->last_request().event.event_type());
EXPECT_EQ(em::ExtensionInstallReportLogEvent::CRX_INSTALL_ERROR_DECLINED,
delegate()->last_request().event.failure_reason());
EXPECT_EQ(em::ExtensionInstallReportLogEvent::KIOSK_MODE_ONLY,
delegate()->last_request().event.crx_install_error_detail());
EXPECT_EQ(em::Extension::TYPE_LEGACY_PACKAGED_APP,
delegate()->last_request().event.extension_type());
EXPECT_TRUE(delegate()->last_request().event.is_misconfiguration_failure());
}
// Verifies that a new event is created when the extension failed to unpack.
......@@ -532,10 +555,11 @@ TEST_F(ExtensionInstallEventLogCollectorTest,
delegate()->last_request().event.manifest_invalid_error());
}
// Verifies that a new event is created when an extension is successfully
// installed.
TEST_F(ExtensionInstallEventLogCollectorTest, InstallExtension) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
// One extension installation succeeded.
auto ext = extensions::ExtensionBuilder(kExtensionName1)
......@@ -553,9 +577,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, InstallExtension) {
// Verifies that a new event is created when the installation stage is changed
// during the installation process.
TEST_F(ExtensionInstallEventLogCollectorTest, InstallationStageChanged) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
// One extension installation succeeded.
auto ext = extensions::ExtensionBuilder(kExtensionName1)
......@@ -578,9 +601,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, InstallationStageChanged) {
// Verifies that a new event is created when the downloading stage is changed
// during the downloading process.
TEST_F(ExtensionInstallEventLogCollectorTest, DownloadingStageChanged) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto ext = extensions::ExtensionBuilder(kExtensionName1)
.SetID(kExtensionId1)
......@@ -616,9 +638,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, DownloadingStageChanged) {
// Verifies that a new event is created when the install creation stage is
// changed.
TEST_F(ExtensionInstallEventLogCollectorTest, InstallCreationStageChanged) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto ext = extensions::ExtensionBuilder(kExtensionName1)
.SetID(kExtensionId1)
......@@ -656,9 +677,8 @@ TEST_F(ExtensionInstallEventLogCollectorTest, InstallCreationStageChanged) {
// Verifies that a new event is created when the cache status is retrieved
// during the extension downloading process.
TEST_F(ExtensionInstallEventLogCollectorTest, DownloadCacheStatusRetrieved) {
std::unique_ptr<ExtensionInstallEventLogCollector> collector =
std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto collector = std::make_unique<ExtensionInstallEventLogCollector>(
registry(), delegate(), profile());
auto ext = extensions::ExtensionBuilder(kExtensionName1)
.SetID(kExtensionId1)
......
......@@ -52,6 +52,7 @@ constexpr char kInstallCreationStage[] = "installCreationStage";
constexpr char kDownloadCacheStatus[] = "downloadCacheStatus";
constexpr char kUnpackerFailureReason[] = "unpackerFailureReason";
constexpr char kManifestInvalidError[] = "manifestInvalidError";
constexpr char kCrxInstallErrorDetail[] = "crxInstallErrorDetail";
// Calculates hash for the given |event| and |context|, and stores the hash in
// |hash|. Returns true if |event| and |context| are json serializable and
......@@ -228,6 +229,12 @@ base::Value ConvertExtensionEventToValue(
extension_install_report_log_event.manifest_invalid_error());
}
if (extension_install_report_log_event.has_crx_install_error_detail()) {
event.SetIntKey(
kCrxInstallErrorDetail,
extension_install_report_log_event.crx_install_error_detail());
}
base::Value wrapper(base::Value::Type::DICTIONARY);
wrapper.SetKey(kExtensionInstallEvent, std::move(event));
......
......@@ -51,6 +51,7 @@ constexpr char kInstallCreationStage[] = "installCreationStage";
constexpr char kDownloadCacheStatus[] = "downloadCacheStatus";
constexpr char kUnpackerFailureReason[] = "unpackerFailureReason";
constexpr char kManifestInvalidError[] = "manifestInvalidError";
constexpr char kCrxInstallErrorDetail[] = "crxInstallErrorDetail";
void ConvertToValueAndVerify(const em::ExtensionInstallReportLogEvent& event,
const std::vector<std::string>& keys) {
......@@ -100,6 +101,25 @@ TEST_F(ExtensionInstallEventLogUtilTest, FailureReasonEvent) {
kExtensionType, kStatefulTotal, kStatefulFree});
}
// Verifies that an event reporting extension installation failure after
// unpacking is successfully parsed.
TEST_F(ExtensionInstallEventLogUtilTest, CrxInstallErrorEvent) {
event_.set_event_type(
em::ExtensionInstallReportLogEvent::INSTALLATION_FAILED);
event_.set_failure_reason(
em::ExtensionInstallReportLogEvent::CRX_INSTALL_ERROR_OTHER);
event_.set_crx_install_error_detail(
em::ExtensionInstallReportLogEvent::UNEXPECTED_ID);
event_.set_is_misconfiguration_failure(false);
event_.set_extension_type(em::Extension_ExtensionType_TYPE_EXTENSION);
event_.set_stateful_total(kDiskSpaceTotalBytes);
event_.set_stateful_free(kDiskSpaceFreeBytes);
ConvertToValueAndVerify(
event_, {kEventType, kFailureReason, kCrxInstallErrorDetail,
kIsMisconfigurationFailure, kExtensionType, kStatefulTotal,
kStatefulFree});
}
// Verifies that an event reporting extension unpack failure is successfully
// parsed.
TEST_F(ExtensionInstallEventLogUtilTest, UnpackerFailureReasonEvent) {
......
......@@ -3083,7 +3083,39 @@ message ExtensionInstallReportLogEvent {
INVALID_VERSION = 12;
BAD_UPDATE_SPECIFICATION = 13;
BAD_APP_STATUS = 14;
};
}
// Extended error code if the extension installation failed due to CRX install
// error. See extensions::CrxInstallErrorDetail for more details.
// extensions::CrxInstallErrorDetail is the main enum and this is a
// copy used for reporting purposes.
enum CrxInstallErrorDetail {
CRX_INSTALL_ERROR_DETAIL_UNKNOWN = 0;
CONVERT_USER_SCRIPT_TO_EXTENSION_FAILED = 1;
UNEXPECTED_ID = 2;
UNEXPECTED_VERSION = 3;
MISMATCHED_VERSION = 4;
CRX_ERROR_MANIFEST_INVALID = 5;
INSTALL_NOT_ENABLED = 6;
OFFSTORE_INSTALL_DISALLOWED = 7;
INCORRECT_APP_CONTENT_TYPE = 8;
NOT_INSTALLED_FROM_GALLERY = 9;
INCORRECT_INSTALL_HOST = 10;
DEPENDENCY_NOT_SHARED_MODULE = 11;
DEPENDENCY_OLD_VERSION = 12;
DEPENDENCY_NOT_ALLOWLISTED = 13;
UNSUPPORTED_REQUIREMENTS = 14;
EXTENSION_IS_BLOCKLISTED = 15;
DISALLOWED_BY_POLICY = 16;
KIOSK_MODE_ONLY = 17;
OVERLAPPING_WEB_EXTENT = 18;
CANT_DOWNGRADE_VERSION = 19;
MOVE_DIRECTORY_TO_PROFILE_FAILED = 20;
CANT_LOAD_EXTENSION = 21;
USER_CANCELED = 22;
USER_ABORTED = 23;
UPDATE_NON_EXISTING_EXTENSION = 24;
}
// Timestamp, in microseconds since epoch. Set for all log
// events.
......@@ -3139,6 +3171,10 @@ message ExtensionInstallReportLogEvent {
// Detailed reason why extension failed due to failure reason
// MANIFEST_INVALID.
optional ManifestInvalidError manifest_invalid_error = 17;
// Extended error code if the extension installation failed due to CRX install
// error.
optional CrxInstallErrorDetail crx_install_error_detail = 18;
}
// A single entry in the push-install log for an app.
......
......@@ -28,7 +28,12 @@ enum class CrxInstallErrorType {
// Extended error code that may help explain the error type.
// Do not change the order of the entries or remove entries in this list.
// Don't forget to update enums.xml when adding new entries.
// 1) Don't forget to update enums.xml when adding new entries.
// 2) Don't forget to update device_management_backend.proto (name:
// ExtensionInstallReportLogEvent::CrxInstallErrorDetail) when adding new
// entries.
// 3) Don't forget to update ConvertCrxInstallErrorDetailToProto method in
// ExtensionInstallEventLogCollector.
enum class CrxInstallErrorDetail {
NONE, // 0
CONVERT_USER_SCRIPT_TO_EXTENSION_FAILED, // 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment