summaryrefslogtreecommitdiff
path: root/chromium/components/device_signals
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-09-29 16:16:15 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-11-09 10:04:06 +0000
commita95a7417ad456115a1ef2da4bb8320531c0821f1 (patch)
treeedcd59279e486d2fd4a8f88a7ed025bcf925c6e6 /chromium/components/device_signals
parent33fc33aa94d4add0878ec30dc818e34e1dd3cc2a (diff)
downloadqtwebengine-chromium-a95a7417ad456115a1ef2da4bb8320531c0821f1.tar.gz
BASELINE: Update Chromium to 106.0.5249.126
Change-Id: Ib0bb21c437a7d1686e21c33f2d329f2ac425b7ab Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/438936 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/device_signals')
-rw-r--r--chromium/components/device_signals/DEPS3
-rw-r--r--chromium/components/device_signals/core/BUILD.gn1
-rw-r--r--chromium/components/device_signals/core/browser/BUILD.gn22
-rw-r--r--chromium/components/device_signals/core/browser/DEPS1
-rw-r--r--chromium/components/device_signals/core/browser/base_signals_collector.cc53
-rw-r--r--chromium/components/device_signals/core/browser/base_signals_collector.h46
-rw-r--r--chromium/components/device_signals/core/browser/file_system_signals_collector.cc73
-rw-r--r--chromium/components/device_signals/core/browser/file_system_signals_collector.h60
-rw-r--r--chromium/components/device_signals/core/browser/file_system_signals_collector_unittest.cc182
-rw-r--r--chromium/components/device_signals/core/browser/metrics_utils.cc86
-rw-r--r--chromium/components/device_signals/core/browser/metrics_utils.h38
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_aggregator.cc12
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_aggregator.h29
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_collector.cc2
-rw-r--r--chromium/components/device_signals/core/browser/mock_signals_collector.h10
-rw-r--r--chromium/components/device_signals/core/browser/mock_system_signals_service_host.h6
-rw-r--r--chromium/components/device_signals/core/browser/mock_user_delegate.h4
-rw-r--r--chromium/components/device_signals/core/browser/pref_names.cc23
-rw-r--r--chromium/components/device_signals/core/browser/pref_names.h19
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator.h26
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl.cc84
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl.h9
-rw-r--r--chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc242
-rw-r--r--chromium/components/device_signals/core/browser/signals_collector.h33
-rw-r--r--chromium/components/device_signals/core/browser/signals_types.cc85
-rw-r--r--chromium/components/device_signals/core/browser/signals_types.h150
-rw-r--r--chromium/components/device_signals/core/browser/user_context.cc2
-rw-r--r--chromium/components/device_signals/core/browser/user_delegate.h17
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service.h15
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl.cc38
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl.h8
-rw-r--r--chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc104
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector.cc99
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector.h65
-rw-r--r--chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc111
-rw-r--r--chromium/components/device_signals/core/common/BUILD.gn33
-rw-r--r--chromium/components/device_signals/core/common/common_types.cc52
-rw-r--r--chromium/components/device_signals/core/common/common_types.h89
-rw-r--r--chromium/components/device_signals/core/common/mojom/BUILD.gn36
-rw-r--r--chromium/components/device_signals/core/common/mojom/DEPS3
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals.mojom57
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.cc104
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.h103
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.cc (renamed from chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc)2
-rw-r--r--chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.h (renamed from chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h)6
-rw-r--r--chromium/components/device_signals/core/common/signals_constants.cc14
-rw-r--r--chromium/components/device_signals/core/common/signals_constants.h5
-rw-r--r--chromium/components/device_signals/core/common/signals_features.cc55
-rw-r--r--chromium/components/device_signals/core/common/signals_features.h36
-rw-r--r--chromium/components/device_signals/core/common/signals_features_unittest.cc89
-rw-r--r--chromium/components/device_signals/core/common/win/BUILD.gn51
-rw-r--r--chromium/components/device_signals/core/common/win/mock_wmi_client.h23
-rw-r--r--chromium/components/device_signals/core/common/win/win_types.cc9
-rw-r--r--chromium/components/device_signals/core/common/win/win_types.h16
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl.cc109
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl.h42
-rw-r--r--chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc120
-rw-r--r--chromium/components/device_signals/core/system_signals/BUILD.gn80
-rw-r--r--chromium/components/device_signals/core/system_signals/base_platform_delegate.cc80
-rw-r--r--chromium/components/device_signals/core/system_signals/base_platform_delegate.h35
-rw-r--r--chromium/components/device_signals/core/system_signals/base_platform_delegate_unittest.cc55
-rw-r--r--chromium/components/device_signals/core/system_signals/file_system_service.cc133
-rw-r--r--chromium/components/device_signals/core/system_signals/file_system_service.h45
-rw-r--r--chromium/components/device_signals/core/system_signals/file_system_service_unittest.cc255
-rw-r--r--chromium/components/device_signals/core/system_signals/hashing_utils.cc48
-rw-r--r--chromium/components/device_signals/core/system_signals/hashing_utils.h24
-rw-r--r--chromium/components/device_signals/core/system_signals/linux/BUILD.gn17
-rw-r--r--chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.cc13
-rw-r--r--chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.h20
-rw-r--r--chromium/components/device_signals/core/system_signals/mac/BUILD.gn17
-rw-r--r--chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.h20
-rw-r--r--chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.mm13
-rw-r--r--chromium/components/device_signals/core/system_signals/mock_file_system_service.cc12
-rw-r--r--chromium/components/device_signals/core/system_signals/mock_file_system_service.h32
-rw-r--r--chromium/components/device_signals/core/system_signals/mock_platform_delegate.cc12
-rw-r--r--chromium/components/device_signals/core/system_signals/mock_platform_delegate.h34
-rw-r--r--chromium/components/device_signals/core/system_signals/platform_delegate.cc23
-rw-r--r--chromium/components/device_signals/core/system_signals/platform_delegate.h52
-rw-r--r--chromium/components/device_signals/core/system_signals/platform_utils.h23
-rw-r--r--chromium/components/device_signals/core/system_signals/posix/BUILD.gn16
-rw-r--r--chromium/components/device_signals/core/system_signals/posix/platform_utils_posix.cc20
-rw-r--r--chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc30
-rw-r--r--chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.h26
-rw-r--r--chromium/components/device_signals/core/system_signals/win/BUILD.gn68
-rw-r--r--chromium/components/device_signals/core/system_signals/win/com_fakes.cc (renamed from chromium/components/device_signals/core/common/win/com_fakes.cc)4
-rw-r--r--chromium/components/device_signals/core/system_signals/win/com_fakes.h (renamed from chromium/components/device_signals/core/common/win/com_fakes.h)8
-rw-r--r--chromium/components/device_signals/core/system_signals/win/mock_wmi_client.cc (renamed from chromium/components/device_signals/core/common/win/mock_wmi_client.cc)4
-rw-r--r--chromium/components/device_signals/core/system_signals/win/mock_wmi_client.h24
-rw-r--r--chromium/components/device_signals/core/system_signals/win/mock_wsc_client.cc (renamed from chromium/components/device_signals/core/common/win/mock_wsc_client.cc)4
-rw-r--r--chromium/components/device_signals/core/system_signals/win/mock_wsc_client.h (renamed from chromium/components/device_signals/core/common/win/mock_wsc_client.h)10
-rw-r--r--chromium/components/device_signals/core/system_signals/win/platform_utils_win.cc31
-rw-r--r--chromium/components/device_signals/core/system_signals/win/win_platform_delegate.cc65
-rw-r--r--chromium/components/device_signals/core/system_signals/win/win_platform_delegate.h24
-rw-r--r--chromium/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc80
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wmi_client.cc (renamed from chromium/components/device_signals/core/common/win/wmi_client.cc)11
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wmi_client.h (renamed from chromium/components/device_signals/core/common/win/wmi_client.h)31
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wmi_client_impl.cc201
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wmi_client_impl.h64
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wmi_client_impl_unittest.cc263
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wsc_client.cc (renamed from chromium/components/device_signals/core/common/win/wsc_client.cc)4
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wsc_client.h (renamed from chromium/components/device_signals/core/common/win/wsc_client.h)14
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wsc_client_impl.cc (renamed from chromium/components/device_signals/core/common/win/wsc_client_impl.cc)18
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wsc_client_impl.h (renamed from chromium/components/device_signals/core/common/win/wsc_client_impl.h)14
-rw-r--r--chromium/components/device_signals/core/system_signals/win/wsc_client_impl_unittest.cc (renamed from chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc)11
104 files changed, 4037 insertions, 863 deletions
diff --git a/chromium/components/device_signals/DEPS b/chromium/components/device_signals/DEPS
new file mode 100644
index 00000000000..a740940b4da
--- /dev/null
+++ b/chromium/components/device_signals/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+crypto",
+] \ No newline at end of file
diff --git a/chromium/components/device_signals/core/BUILD.gn b/chromium/components/device_signals/core/BUILD.gn
index 3c3c530e82f..0fc3e5256de 100644
--- a/chromium/components/device_signals/core/BUILD.gn
+++ b/chromium/components/device_signals/core/BUILD.gn
@@ -7,5 +7,6 @@ source_set("unit_tests") {
deps = [
"//components/device_signals/core/browser:unit_tests",
"//components/device_signals/core/common:unit_tests",
+ "//components/device_signals/core/system_signals:unit_tests",
]
}
diff --git a/chromium/components/device_signals/core/browser/BUILD.gn b/chromium/components/device_signals/core/browser/BUILD.gn
index a12e34d6503..abfd3a2d7cd 100644
--- a/chromium/components/device_signals/core/browser/BUILD.gn
+++ b/chromium/components/device_signals/core/browser/BUILD.gn
@@ -4,9 +4,13 @@
static_library("browser") {
public = [
+ "base_signals_collector.h",
+ "file_system_signals_collector.h",
+ "metrics_utils.h",
"signals_aggregator.h",
"signals_aggregator_impl.h",
"signals_collector.h",
+ "signals_types.h",
"system_signals_service_host.h",
"user_context.h",
"user_delegate.h",
@@ -15,7 +19,11 @@ static_library("browser") {
]
sources = [
+ "base_signals_collector.cc",
+ "file_system_signals_collector.cc",
+ "metrics_utils.cc",
"signals_aggregator_impl.cc",
+ "signals_types.cc",
"user_context.cc",
"user_permission_service_impl.cc",
]
@@ -24,18 +32,31 @@ static_library("browser") {
"//base",
"//components/device_signals/core/common/mojom",
"//components/keyed_service/core",
+ "//third_party/abseil-cpp:absl",
]
deps = [
"//components/device_signals/core/common",
"//components/policy/core/common",
+ "//components/prefs",
"//components/signin/public/identity_manager",
]
+
+ if (is_win) {
+ public_deps += [ "//components/device_signals/core/common/win" ]
+ }
+
+ if (is_win || is_linux || is_mac) {
+ public += [ "pref_names.h" ]
+ sources += [ "pref_names.cc" ]
+ }
}
static_library("test_support") {
testonly = true
sources = [
+ "mock_signals_aggregator.cc",
+ "mock_signals_aggregator.h",
"mock_signals_collector.cc",
"mock_signals_collector.h",
"mock_system_signals_service_host.cc",
@@ -58,6 +79,7 @@ static_library("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
+ "file_system_signals_collector_unittest.cc",
"signals_aggregator_impl_unittest.cc",
"user_permission_service_impl_unittest.cc",
]
diff --git a/chromium/components/device_signals/core/browser/DEPS b/chromium/components/device_signals/core/browser/DEPS
index 97d436ae7d8..b5db53237dd 100644
--- a/chromium/components/device_signals/core/browser/DEPS
+++ b/chromium/components/device_signals/core/browser/DEPS
@@ -2,4 +2,5 @@ include_rules = [
"+components/keyed_service/core",
"+components/signin/public",
"+components/policy/core",
+ "+components/prefs",
]
diff --git a/chromium/components/device_signals/core/browser/base_signals_collector.cc b/chromium/components/device_signals/core/browser/base_signals_collector.cc
new file mode 100644
index 00000000000..584f9af9823
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/base_signals_collector.cc
@@ -0,0 +1,53 @@
+// 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 "components/device_signals/core/browser/base_signals_collector.h"
+
+#include <unordered_set>
+#include <utility>
+
+#include "base/callback.h"
+#include "components/device_signals/core/browser/signals_types.h"
+
+namespace device_signals {
+
+BaseSignalsCollector::BaseSignalsCollector(
+ const std::unordered_map<const SignalName, GetSignalCallback>
+ signals_collection_map)
+ : signals_collection_map_(std::move(signals_collection_map)) {
+ DCHECK(!signals_collection_map_.empty());
+}
+
+BaseSignalsCollector::~BaseSignalsCollector() = default;
+
+bool BaseSignalsCollector::IsSignalSupported(SignalName signal_name) {
+ const auto it = signals_collection_map_.find(signal_name);
+ return it != signals_collection_map_.end();
+}
+
+const std::unordered_set<SignalName>
+BaseSignalsCollector::GetSupportedSignalNames() {
+ std::unordered_set<SignalName> supported_signals;
+ for (const auto& collection_pair : signals_collection_map_) {
+ supported_signals.insert(collection_pair.first);
+ }
+
+ return supported_signals;
+}
+
+void BaseSignalsCollector::GetSignal(SignalName signal_name,
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) {
+ if (!IsSignalSupported(signal_name)) {
+ response.top_level_error = SignalCollectionError::kUnsupported;
+ std::move(done_closure).Run();
+ return;
+ }
+
+ signals_collection_map_[signal_name].Run(request, response,
+ std::move(done_closure));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/base_signals_collector.h b/chromium/components/device_signals/core/browser/base_signals_collector.h
new file mode 100644
index 00000000000..82b9696af6d
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/base_signals_collector.h
@@ -0,0 +1,46 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_BASE_SIGNALS_COLLECTOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_BASE_SIGNALS_COLLECTOR_H_
+
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "components/device_signals/core/browser/signals_collector.h"
+
+namespace device_signals {
+
+class BaseSignalsCollector : public SignalsCollector {
+ public:
+ ~BaseSignalsCollector() override;
+
+ // SignalsCollector:
+ bool IsSignalSupported(SignalName signal_name) override;
+ const std::unordered_set<SignalName> GetSupportedSignalNames() override;
+ void GetSignal(SignalName signal_name,
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) override;
+
+ protected:
+ using GetSignalCallback =
+ base::RepeatingCallback<void(const SignalsAggregationRequest&,
+ SignalsAggregationResponse&,
+ base::OnceClosure)>;
+
+ explicit BaseSignalsCollector(
+ const std::unordered_map<const SignalName, GetSignalCallback>
+ signals_collection_map);
+
+ private:
+ // Map used to forward signal collection requests to the right function keyed
+ // from a given signal name.
+ std::unordered_map<const SignalName, GetSignalCallback>
+ signals_collection_map_;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_BASE_SIGNALS_COLLECTOR_H_
diff --git a/chromium/components/device_signals/core/browser/file_system_signals_collector.cc b/chromium/components/device_signals/core/browser/file_system_signals_collector.cc
new file mode 100644
index 00000000000..b7e529d0517
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/file_system_signals_collector.cc
@@ -0,0 +1,73 @@
+// 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 "components/device_signals/core/browser/file_system_signals_collector.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "components/device_signals/core/browser/signals_types.h"
+#include "components/device_signals/core/browser/system_signals_service_host.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/common/mojom/system_signals.mojom.h"
+
+namespace device_signals {
+
+FileSystemSignalsCollector::FileSystemSignalsCollector(
+ SystemSignalsServiceHost* system_service_host)
+ : BaseSignalsCollector({
+ {SignalName::kFileSystemInfo,
+ base::BindRepeating(
+ &FileSystemSignalsCollector::GetFileSystemInfoSignal,
+ base::Unretained(this))},
+ }),
+ system_service_host_(system_service_host) {
+ DCHECK(system_service_host_);
+}
+
+FileSystemSignalsCollector::~FileSystemSignalsCollector() = default;
+
+void FileSystemSignalsCollector::GetFileSystemInfoSignal(
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) {
+ if (request.file_system_signal_parameters.empty()) {
+ FileSystemInfoResponse signal_response;
+ signal_response.collection_error =
+ SignalCollectionError::kMissingParameters;
+ response.file_system_info_response = std::move(signal_response);
+ std::move(done_closure).Run();
+ return;
+ }
+
+ auto* system_signals_service = system_service_host_->GetService();
+ if (!system_signals_service) {
+ FileSystemInfoResponse signal_response;
+ signal_response.collection_error =
+ SignalCollectionError::kMissingSystemService;
+ response.file_system_info_response = std::move(signal_response);
+ std::move(done_closure).Run();
+ return;
+ }
+
+ system_signals_service->GetFileSystemSignals(
+ request.file_system_signal_parameters,
+ base::BindOnce(&FileSystemSignalsCollector::OnFileSystemSignalCollected,
+ weak_factory_.GetWeakPtr(), std::ref(response),
+ std::move(done_closure)));
+}
+
+void FileSystemSignalsCollector::OnFileSystemSignalCollected(
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
+ const std::vector<FileSystemItem>& file_system_items) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ FileSystemInfoResponse signal_response;
+ signal_response.file_system_items = std::move(file_system_items);
+ response.file_system_info_response = std::move(signal_response);
+ std::move(done_closure).Run();
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/file_system_signals_collector.h b/chromium/components/device_signals/core/browser/file_system_signals_collector.h
new file mode 100644
index 00000000000..0b70b429a90
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/file_system_signals_collector.h
@@ -0,0 +1,60 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_FILE_SYSTEM_SIGNALS_COLLECTOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_FILE_SYSTEM_SIGNALS_COLLECTOR_H_
+
+#include <map>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/device_signals/core/browser/base_signals_collector.h"
+
+namespace device_signals {
+
+struct FileSystemItem;
+class SystemSignalsServiceHost;
+
+// Collector in charge of collecting device signals that live in the file
+// system.
+class FileSystemSignalsCollector : public BaseSignalsCollector {
+ public:
+ explicit FileSystemSignalsCollector(
+ SystemSignalsServiceHost* system_service_host);
+
+ ~FileSystemSignalsCollector() override;
+
+ FileSystemSignalsCollector(const FileSystemSignalsCollector&) = delete;
+ FileSystemSignalsCollector& operator=(const FileSystemSignalsCollector&) =
+ delete;
+
+ private:
+ // Collection function for the File System Info signal. `request` must contain
+ // the required parameters for this signal. `response` will be passed along
+ // and the signal values will be set on it when available. `done_closure` will
+ // be invoked when signal collection is complete.
+ void GetFileSystemInfoSignal(const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure);
+
+ // Invoked when the SystemSignalsService returns the collected File System
+ // items' signals as `file_system_items`. Will update `response` with the
+ // signal collection outcome, and invoke `done_closure` to asynchronously
+ // notify the caller of the completion of this request.
+ void OnFileSystemSignalCollected(
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
+ const std::vector<FileSystemItem>& file_system_items);
+
+ // Instance used to retrieve a pointer to a SystemSignalsService instance.
+ base::raw_ptr<SystemSignalsServiceHost> system_service_host_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ base::WeakPtrFactory<FileSystemSignalsCollector> weak_factory_{this};
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_FILE_SYSTEM_SIGNALS_COLLECTOR_H_
diff --git a/chromium/components/device_signals/core/browser/file_system_signals_collector_unittest.cc b/chromium/components/device_signals/core/browser/file_system_signals_collector_unittest.cc
new file mode 100644
index 00000000000..409be2fa098
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/file_system_signals_collector_unittest.cc
@@ -0,0 +1,182 @@
+// 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 "components/device_signals/core/browser/file_system_signals_collector.h"
+
+#include <array>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "components/device_signals/core/browser/mock_system_signals_service_host.h"
+#include "components/device_signals/core/browser/signals_types.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/common/signals_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::ContainerEq;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace device_signals {
+
+namespace {
+
+SignalsAggregationRequest CreateRequest(SignalName signal_name,
+ bool with_file_parameter = true) {
+ SignalsAggregationRequest request;
+ request.signal_names.emplace(signal_name);
+
+ if (with_file_parameter) {
+ GetFileSystemInfoOptions options1;
+ options1.file_path = base::FilePath::FromUTF8Unsafe("some file path");
+ options1.compute_sha256 = true;
+ options1.compute_executable_metadata = true;
+
+ GetFileSystemInfoOptions options2;
+ options2.file_path = base::FilePath::FromUTF8Unsafe("some file path");
+ options2.compute_sha256 = true;
+ options2.compute_executable_metadata = true;
+
+ request.file_system_signal_parameters.push_back(options1);
+ request.file_system_signal_parameters.push_back(options2);
+ }
+
+ return request;
+}
+} // namespace
+
+using GetFileSystemSignalsCallback =
+ MockSystemSignalsService::GetFileSystemSignalsCallback;
+
+class FileSystemSignalsCollectorTest : public testing::Test {
+ protected:
+ FileSystemSignalsCollectorTest() : signal_collector_(&service_host_) {
+ ON_CALL(service_host_, GetService()).WillByDefault(Return(&service_));
+ }
+
+ base::test::TaskEnvironment task_environment_;
+
+ StrictMock<MockSystemSignalsServiceHost> service_host_;
+ StrictMock<MockSystemSignalsService> service_;
+ FileSystemSignalsCollector signal_collector_;
+};
+
+// Test that runs a sanity check on the set of signals supported by this
+// collector. Will need to be updated if new signals become supported.
+TEST_F(FileSystemSignalsCollectorTest, SupportedSignalNames) {
+ const std::array<SignalName, 1> supported_signals{
+ {SignalName::kFileSystemInfo}};
+
+ const auto names_set = signal_collector_.GetSupportedSignalNames();
+
+ EXPECT_EQ(names_set.size(), supported_signals.size());
+ for (const auto& signal_name : supported_signals) {
+ EXPECT_TRUE(names_set.find(signal_name) != names_set.end());
+ }
+}
+
+// Tests that an unsupported signal is marked as unsupported.
+TEST_F(FileSystemSignalsCollectorTest, GetSignal_Unsupported) {
+ SignalName signal_name = SignalName::kAntiVirus;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ signal_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(),
+ SignalCollectionError::kUnsupported);
+}
+
+// Tests that the request does not contain the required parameters for the
+// File System signal.
+TEST_F(FileSystemSignalsCollectorTest, GetSignal_File_MissingParameters) {
+ SignalName signal_name = SignalName::kFileSystemInfo;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ signal_collector_.GetSignal(
+ signal_name, CreateRequest(signal_name, /*with_file_parameter=*/false),
+ response, run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.file_system_info_response.has_value());
+ ASSERT_TRUE(response.file_system_info_response->collection_error.has_value());
+ EXPECT_EQ(response.file_system_info_response->collection_error.value(),
+ SignalCollectionError::kMissingParameters);
+}
+
+// Tests that not being able to retrieve a pointer to the SystemSignalsService
+// returns an error.
+TEST_F(FileSystemSignalsCollectorTest,
+ GetSignal_File_MissingSystemSignalsService) {
+ EXPECT_CALL(service_host_, GetService()).WillOnce(Return(nullptr));
+
+ SignalName signal_name = SignalName::kFileSystemInfo;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ signal_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.file_system_info_response.has_value());
+ ASSERT_TRUE(response.file_system_info_response->collection_error.has_value());
+ EXPECT_EQ(response.file_system_info_response->collection_error.value(),
+ SignalCollectionError::kMissingSystemService);
+}
+
+// Tests a successful File System signal retrieval.
+TEST_F(FileSystemSignalsCollectorTest, GetSignal_FileSystemInfo) {
+ // Can be any value really.
+ FileSystemItem retrieved_item;
+ retrieved_item.file_path =
+ base::FilePath::FromUTF8Unsafe("some successful file path");
+ retrieved_item.presence = PresenceValue::kFound;
+
+ std::vector<FileSystemItem> file_system_items;
+ file_system_items.push_back(retrieved_item);
+
+ SignalName signal_name = SignalName::kFileSystemInfo;
+ auto request = CreateRequest(signal_name);
+
+ EXPECT_CALL(service_host_, GetService()).Times(1);
+ EXPECT_CALL(service_,
+ GetFileSystemSignals(
+ ContainerEq(request.file_system_signal_parameters), _))
+ .WillOnce(Invoke(
+ [&file_system_items](
+ const std::vector<GetFileSystemInfoOptions> signal_parameters,
+ GetFileSystemSignalsCallback signal_callback) {
+ std::move(signal_callback).Run(file_system_items);
+ }));
+
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ signal_collector_.GetSignal(signal_name, request, response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ EXPECT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.file_system_info_response.has_value());
+ EXPECT_FALSE(
+ response.file_system_info_response->collection_error.has_value());
+ EXPECT_EQ(response.file_system_info_response->file_system_items.size(),
+ file_system_items.size());
+ EXPECT_EQ(response.file_system_info_response->file_system_items[0],
+ file_system_items[0]);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/metrics_utils.cc b/chromium/components/device_signals/core/browser/metrics_utils.cc
new file mode 100644
index 00000000000..02d955c1f2a
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/metrics_utils.cc
@@ -0,0 +1,86 @@
+// 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 "components/device_signals/core/browser/metrics_utils.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/stringprintf.h"
+#include "components/device_signals/core/browser/signals_types.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
+
+namespace device_signals {
+
+namespace {
+
+constexpr int kMaxSampleValue = 100;
+
+constexpr char kUserPermissionHistogram[] =
+ "Enterprise.DeviceSignals.UserPermission";
+constexpr char kCollectionRequestHistogram[] =
+ "Enterprise.DeviceSignals.Collection.Request";
+constexpr char kCollectionSuccessHistogram[] =
+ "Enterprise.DeviceSignals.Collection.Success";
+constexpr char kCollectionFailureHistogram[] =
+ "Enterprise.DeviceSignals.Collection.Failure";
+
+constexpr char kCollectionSuccessSizeHistogramFormat[] =
+ "Enterprise.DeviceSignals.Collection.Success.%s.Items";
+constexpr char kCollectionSpecificFailureHistogramFormat[] =
+ "Enterprise.DeviceSignals.Collection.Failure.%sLevelError";
+
+std::string GetHistogramVariant(SignalName signal_name) {
+ switch (signal_name) {
+ case SignalName::kAntiVirus:
+ return "AntiVirus";
+ case SignalName::kHotfixes:
+ return "Hotfixes";
+ case SignalName::kFileSystemInfo:
+ return "FileSystemInfo";
+ case SignalName::kSystemSettings:
+ return "SystemSettings";
+ }
+}
+
+std::string GetErrorHistogramVariant(SignalName signal_name,
+ bool is_top_level_error) {
+ return base::StringPrintf("%s.%s", GetHistogramVariant(signal_name).c_str(),
+ is_top_level_error ? "Top" : "Collection");
+}
+
+} // namespace
+
+void LogUserPermissionChecked(UserPermission permission) {
+ base::UmaHistogramEnumeration(kUserPermissionHistogram, permission);
+}
+
+void LogSignalCollectionRequested(SignalName signal_name) {
+ base::UmaHistogramEnumeration(kCollectionRequestHistogram, signal_name);
+}
+
+void LogSignalCollectionFailed(SignalName signal_name,
+ SignalCollectionError error,
+ bool is_top_level_error) {
+ base::UmaHistogramEnumeration(kCollectionFailureHistogram, signal_name);
+
+ base::UmaHistogramEnumeration(
+ base::StringPrintf(
+ kCollectionSpecificFailureHistogramFormat,
+ GetErrorHistogramVariant(signal_name, is_top_level_error).c_str()),
+ error);
+}
+
+void LogSignalCollectionSucceeded(
+ SignalName signal_name,
+ absl::optional<size_t> signal_collection_size) {
+ base::UmaHistogramEnumeration(kCollectionSuccessHistogram, signal_name);
+
+ if (signal_collection_size.has_value()) {
+ base::UmaHistogramExactLinear(
+ base::StringPrintf(kCollectionSuccessSizeHistogramFormat,
+ GetHistogramVariant(signal_name).c_str()),
+ signal_collection_size.value(), kMaxSampleValue);
+ }
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/metrics_utils.h b/chromium/components/device_signals/core/browser/metrics_utils.h
new file mode 100644
index 00000000000..67fbc59af3b
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/metrics_utils.h
@@ -0,0 +1,38 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_METRICS_UTILS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_METRICS_UTILS_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace device_signals {
+
+enum class SignalCollectionError;
+enum class SignalName;
+enum class UserPermission;
+
+// Records that `permission` was the outcome of a permission check.
+void LogUserPermissionChecked(UserPermission permission);
+
+// Records that a request to collect `signal_name` was received.
+void LogSignalCollectionRequested(SignalName signal_name);
+
+// Records that the collection of the signal `signal_name` failed with `error`,
+// which, based on `is_top_level_error`, is either a top-level aggregation
+// error, or a collection-level error.
+void LogSignalCollectionFailed(SignalName signal_name,
+ SignalCollectionError error,
+ bool is_top_level_error);
+
+// Records that the collection of the signal `signal_name` was successful.
+// If applicable, will also record the number of items collected as given by
+// `signal_collection_size`.
+void LogSignalCollectionSucceeded(
+ SignalName signal_name,
+ absl::optional<size_t> signal_collection_size);
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_METRICS_UTILS_H_
diff --git a/chromium/components/device_signals/core/browser/mock_signals_aggregator.cc b/chromium/components/device_signals/core/browser/mock_signals_aggregator.cc
new file mode 100644
index 00000000000..46db9f5c545
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_signals_aggregator.cc
@@ -0,0 +1,12 @@
+// 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 "components/device_signals/core/browser/mock_signals_aggregator.h"
+
+namespace device_signals {
+
+MockSignalsAggregator::MockSignalsAggregator() = default;
+MockSignalsAggregator::~MockSignalsAggregator() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/mock_signals_aggregator.h b/chromium/components/device_signals/core/browser/mock_signals_aggregator.h
new file mode 100644
index 00000000000..77284d91452
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/mock_signals_aggregator.h
@@ -0,0 +1,29 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_AGGREGATOR_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_AGGREGATOR_H_
+
+#include "base/callback.h"
+#include "components/device_signals/core/browser/signals_aggregator.h"
+#include "components/device_signals/core/browser/signals_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockSignalsAggregator : public SignalsAggregator {
+ public:
+ MockSignalsAggregator();
+ ~MockSignalsAggregator() override;
+
+ MOCK_METHOD(void,
+ GetSignals,
+ (const SignalsAggregationRequest&,
+ SignalsAggregator::GetSignalsCallback),
+ (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_SIGNALS_AGGREGATOR_H_
diff --git a/chromium/components/device_signals/core/browser/mock_signals_collector.cc b/chromium/components/device_signals/core/browser/mock_signals_collector.cc
index dd569e50e9f..e6b11d90e48 100644
--- a/chromium/components/device_signals/core/browser/mock_signals_collector.cc
+++ b/chromium/components/device_signals/core/browser/mock_signals_collector.cc
@@ -4,6 +4,8 @@
#include "components/device_signals/core/browser/mock_signals_collector.h"
+#include "components/device_signals/core/browser/signals_types.h"
+
namespace device_signals {
MockSignalsCollector::MockSignalsCollector() = default;
diff --git a/chromium/components/device_signals/core/browser/mock_signals_collector.h b/chromium/components/device_signals/core/browser/mock_signals_collector.h
index cb3e1172573..63db9597dbc 100644
--- a/chromium/components/device_signals/core/browser/mock_signals_collector.h
+++ b/chromium/components/device_signals/core/browser/mock_signals_collector.h
@@ -17,15 +17,17 @@ class MockSignalsCollector : public SignalsCollector {
MockSignalsCollector();
~MockSignalsCollector() override;
- MOCK_METHOD(const std::unordered_set<std::string>,
+ MOCK_METHOD(bool, IsSignalSupported, (SignalName), (override));
+ MOCK_METHOD(const std::unordered_set<SignalName>,
GetSupportedSignalNames,
(),
(override));
MOCK_METHOD(void,
GetSignal,
- (const std::string&,
- const base::Value&,
- SignalsCollector::GetSignalCallback),
+ (SignalName signal_name,
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure),
(override));
};
diff --git a/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h
index b5278eefcda..51aa57f77bf 100644
--- a/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h
+++ b/chromium/components/device_signals/core/browser/mock_system_signals_service_host.h
@@ -26,9 +26,9 @@ class MockSystemSignalsService : public mojom::SystemSignalsService {
~MockSystemSignalsService() override;
MOCK_METHOD(void,
- GetBinarySignals,
- (std::vector<device_signals::mojom::BinarySignalsRequestPtr>,
- GetBinarySignalsCallback),
+ GetFileSystemSignals,
+ (const std::vector<device_signals::GetFileSystemInfoOptions>&,
+ GetFileSystemSignalsCallback),
(override));
#if BUILDFLAG(IS_WIN)
diff --git a/chromium/components/device_signals/core/browser/mock_user_delegate.h b/chromium/components/device_signals/core/browser/mock_user_delegate.h
index c620f3aa8c2..ea1560d48b9 100644
--- a/chromium/components/device_signals/core/browser/mock_user_delegate.h
+++ b/chromium/components/device_signals/core/browser/mock_user_delegate.h
@@ -6,7 +6,6 @@
#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_MOCK_USER_DELEGATE_H_
#include "components/device_signals/core/browser/user_delegate.h"
-#include "components/signin/public/identity_manager/account_info.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace device_signals {
@@ -17,7 +16,8 @@ class MockUserDelegate : public UserDelegate {
~MockUserDelegate() override;
MOCK_METHOD(bool, IsAffiliated, (), (const, override));
- MOCK_METHOD(bool, IsSameManagedUser, (const AccountInfo&), (const, override));
+ MOCK_METHOD(bool, IsManaged, (), (const, override));
+ MOCK_METHOD(bool, IsSameUser, (const std::string&), (const, override));
};
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/pref_names.cc b/chromium/components/device_signals/core/browser/pref_names.cc
new file mode 100644
index 00000000000..d85093ee3e4
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/pref_names.cc
@@ -0,0 +1,23 @@
+// 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 "components/device_signals/core/browser/pref_names.h"
+
+#include "components/prefs/pref_registry_simple.h"
+
+namespace device_signals {
+namespace prefs {
+
+// Whether or not admin requires managed users to share device signals when they
+// sign in on an unmanaged device
+const char kUnmanagedDeviceSignalsConsentFlowEnabled[] =
+ "device_signals.consent_collection_enabled";
+
+} // namespace prefs
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kUnmanagedDeviceSignalsConsentFlowEnabled, false);
+}
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/pref_names.h b/chromium/components/device_signals/core/browser/pref_names.h
new file mode 100644
index 00000000000..ad1ffb22ab8
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/pref_names.h
@@ -0,0 +1,19 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_PREF_NAMES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_PREF_NAMES_H_
+
+class PrefRegistrySimple;
+
+namespace device_signals {
+namespace prefs {
+extern const char kUnmanagedDeviceSignalsConsentFlowEnabled[];
+} // namespace prefs
+
+// Registers user preferences related to Device Signal Sharing.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_PREF_NAMES_H_
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator.h b/chromium/components/device_signals/core/browser/signals_aggregator.h
index 6ff18d6ebad..2fe08085160 100644
--- a/chromium/components/device_signals/core/browser/signals_aggregator.h
+++ b/chromium/components/device_signals/core/browser/signals_aggregator.h
@@ -6,31 +6,27 @@
#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_AGGREGATOR_H_
#include "base/callback_forward.h"
-#include "base/values.h"
#include "components/keyed_service/core/keyed_service.h"
namespace device_signals {
-struct UserContext;
+struct SignalsAggregationRequest;
+struct SignalsAggregationResponse;
class SignalsAggregator : public KeyedService {
public:
- using GetSignalsCallback = base::OnceCallback<void(base::Value)>;
+ using GetSignalsCallback =
+ base::OnceCallback<void(SignalsAggregationResponse)>;
~SignalsAggregator() override = default;
- // Will asynchronously collect signals as defined in the `parameters`
- // dictionary, where keys represent the names of the signals to be collected
- // and values represent their collection parameters. Invokes `callback` with
- // the collected signals stored in a dictionary, where the keys are the
- // signal names and values are the collected values. If signal collection
- // failed, the value returned in the callback may be solely an error string.
- // Will use `user_context` to validate that the user has permissions to the
- // device's signals.
- // Currently only supports the collection of one signal (only one entry in
- // `parameter`).
- virtual void GetSignals(const UserContext& user_context,
- base::Value::Dict parameters,
+ // Will asynchronously collect signals whose names are specified in the
+ // `request` object, and will also use its user context to validate that the
+ // user has permissions to the device's signals. Invokes `callback` with the
+ // collected signals. Currently only supports the collection of one signal
+ // (only one entry in `request.signal_names`) for simplicity as no current use
+ // case require supporting collecting multiple signals in one request.
+ virtual void GetSignals(const SignalsAggregationRequest& request,
GetSignalsCallback callback) = 0;
};
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc b/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc
index f2cb8044188..9c4e3fa756d 100644
--- a/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl.cc
@@ -1,15 +1,19 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/browser/signals_aggregator_impl.h"
+#include <functional>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/strings/string_util.h"
#include "base/values.h"
+#include "components/device_signals/core/browser/metrics_utils.h"
#include "components/device_signals/core/browser/signals_collector.h"
+#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/browser/user_context.h"
#include "components/device_signals/core/browser/user_permission_service.h"
#include "components/device_signals/core/common/signals_constants.h"
@@ -18,21 +22,35 @@ namespace device_signals {
namespace {
-std::string PermissionToError(const UserPermission permission) {
+SignalCollectionError PermissionToError(const UserPermission permission) {
switch (permission) {
case UserPermission::kUnaffiliated:
- return errors::kUnaffiliatedUser;
+ return SignalCollectionError::kUnaffiliatedUser;
case UserPermission::kMissingConsent:
- return errors::kConsentRequired;
+ return SignalCollectionError::kConsentRequired;
case UserPermission::kConsumerUser:
case UserPermission::kUnknownUser:
- return errors::kUnsupported;
+ case UserPermission::kMissingUser:
+ return SignalCollectionError::kInvalidUser;
case UserPermission::kGranted:
NOTREACHED();
- return "";
+ return SignalCollectionError::kUnsupported;
}
}
+void RespondWithError(SignalCollectionError error,
+ SignalsAggregator::GetSignalsCallback callback) {
+ SignalsAggregationResponse response;
+ response.top_level_error = error;
+ std::move(callback).Run(std::move(response));
+}
+
+void OnSignalRetrieved(std::unique_ptr<SignalsAggregationResponse> response,
+ SignalsAggregator::GetSignalsCallback callback) {
+ DCHECK(response);
+ std::move(callback).Run(std::move(*response));
+}
+
} // namespace
SignalsAggregatorImpl::SignalsAggregatorImpl(
@@ -45,62 +63,60 @@ SignalsAggregatorImpl::SignalsAggregatorImpl(
SignalsAggregatorImpl::~SignalsAggregatorImpl() = default;
-void SignalsAggregatorImpl::GetSignals(const UserContext& user_context,
- base::Value::Dict parameters,
+void SignalsAggregatorImpl::GetSignals(const SignalsAggregationRequest& request,
GetSignalsCallback callback) {
- if (parameters.empty()) {
- std::move(callback).Run(base::Value(errors::kUnsupported));
+ // Request for collection of multiple signals is not yet supported. Only the
+ // first signal will be returned.
+ if (request.signal_names.size() != 1) {
+ RespondWithError(SignalCollectionError::kUnsupported, std::move(callback));
return;
}
- // Request for collection of multiple signals is not yet supported. Only the
- // first signal will be returned.
- DCHECK(parameters.size() == 1);
+ LogSignalCollectionRequested(*request.signal_names.begin());
+ // Capture a reference to `user_context` before calling the function to
+ // prevent a "use after move" warning on `request`, since we cannot guarantee
+ // the order of execution when evaluation function parameters.
+ const auto& user_context = request.user_context;
permission_service_->CanCollectSignals(
user_context,
base::BindOnce(&SignalsAggregatorImpl::OnUserPermissionChecked,
- weak_factory_.GetWeakPtr(), std::move(parameters),
+ weak_factory_.GetWeakPtr(), std::move(request),
std::move(callback)));
}
void SignalsAggregatorImpl::OnUserPermissionChecked(
- base::Value::Dict parameters,
+ const SignalsAggregationRequest& request,
GetSignalsCallback callback,
const UserPermission user_permission) {
+ LogUserPermissionChecked(user_permission);
if (user_permission != UserPermission::kGranted) {
- std::move(callback).Run(base::Value(PermissionToError(user_permission)));
+ RespondWithError(PermissionToError(user_permission), std::move(callback));
return;
}
- std::pair<const std::string&, const base::Value&> signal_request =
- *parameters.begin();
+ SignalName signal_name = *request.signal_names.begin();
for (const auto& collector : collectors_) {
- const auto signals_set = collector->GetSupportedSignalNames();
- if (signals_set.find(signal_request.first) == signals_set.end()) {
+ if (!collector->IsSignalSupported(signal_name)) {
// Signal is not supported by current collector.
continue;
}
+ // Create `response` on the heap to prevent memory access errors from
+ // occurring in the collectors.
+ auto response = std::make_unique<SignalsAggregationResponse>();
+ SignalsAggregationResponse* response_ptr = response.get();
+
// Signal is supported by current collector.
- auto return_callback = base::BindOnce(
- &SignalsAggregatorImpl::OnSignalCollected, weak_factory_.GetWeakPtr(),
- signal_request.first, std::move(callback));
- collector->GetSignal(signal_request.first, signal_request.second,
- std::move(return_callback));
+ auto done_closure = base::BindOnce(&OnSignalRetrieved, std::move(response),
+ std::move(callback));
+ collector->GetSignal(signal_name, request, *response_ptr,
+ std::move(done_closure));
return;
}
// Not a supported signal.
- std::move(callback).Run(base::Value(errors::kUnsupported));
-}
-
-void SignalsAggregatorImpl::OnSignalCollected(const std::string signal_name,
- GetSignalsCallback callback,
- base::Value value) {
- base::Value::Dict return_value;
- return_value.Set(signal_name, std::move(value));
- std::move(callback).Run(base::Value(std::move(return_value)));
+ RespondWithError(SignalCollectionError::kUnsupported, std::move(callback));
}
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl.h b/chromium/components/device_signals/core/browser/signals_aggregator_impl.h
index 949b7cb920b..3d6b6b33549 100644
--- a/chromium/components/device_signals/core/browser/signals_aggregator_impl.h
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl.h
@@ -29,19 +29,14 @@ class SignalsAggregatorImpl : public SignalsAggregator {
~SignalsAggregatorImpl() override;
// SignalsAggregator:
- void GetSignals(const UserContext& user_context,
- base::Value::Dict parameters,
+ void GetSignals(const SignalsAggregationRequest& request,
GetSignalsCallback callback) override;
private:
- void OnUserPermissionChecked(base::Value::Dict parameters,
+ void OnUserPermissionChecked(const SignalsAggregationRequest& request,
GetSignalsCallback callback,
const UserPermission user_permission);
- void OnSignalCollected(const std::string signal_name,
- GetSignalsCallback callback,
- base::Value value);
-
base::raw_ptr<UserPermissionService> permission_service_;
std::vector<std::unique_ptr<SignalsCollector>> collectors_;
diff --git a/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc b/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc
index 42d9c3c5d97..45065984dc5 100644
--- a/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc
+++ b/chromium/components/device_signals/core/browser/signals_aggregator_impl_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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.
@@ -9,10 +9,12 @@
#include <vector>
#include "base/memory/raw_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "components/device_signals/core/browser/mock_signals_collector.h"
#include "components/device_signals/core/browser/mock_user_permission_service.h"
+#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/browser/user_context.h"
#include "components/device_signals/core/browser/user_permission_service.h"
#include "components/device_signals/core/common/signals_constants.h"
@@ -29,79 +31,22 @@ namespace device_signals {
namespace {
-constexpr char kFakeSignalName[] = "signal_name";
-constexpr char kOtherFakeSignalName[] = "other_signal_name";
-
constexpr char kGaiaId[] = "gaia-id";
-base::Value GetFakeSignalParameter() {
- return base::Value("some parameter needed by the signal collector");
-}
-
-base::Value GetFakeSignalValue() {
- base::Value::Dict fake_signal_value;
- fake_signal_value.Set("some_key", "some_value");
- return base::Value(std::move(fake_signal_value));
-}
-
-base::Value GetOtherFakeSignalParameter() {
- base::Value::Dict fake_signal_value;
- fake_signal_value.Set("some_file_path", "some/path");
- return base::Value(std::move(fake_signal_value));
-}
-
-base::Value GetOtherFakeSignalValue() {
- return base::Value("just a string");
-}
-
-std::unique_ptr<MockSignalsCollector> GetCollectorForFakeSignal() {
- auto mock_collector = std::make_unique<MockSignalsCollector>();
- ON_CALL(*mock_collector.get(), GetSupportedSignalNames())
- .WillByDefault(
- Return(std::unordered_set<std::string>({kFakeSignalName})));
-
- ON_CALL(*mock_collector.get(), GetSignal(kFakeSignalName, _, _))
- .WillByDefault(
- Invoke([](const std::string& signal_name, const base::Value& params,
- SignalsCollector::GetSignalCallback callback) {
- EXPECT_EQ(params, GetFakeSignalParameter());
- std::move(callback).Run(GetFakeSignalValue());
- }));
-
- return mock_collector;
-}
-
-std::unique_ptr<MockSignalsCollector> GetCollectorForOtherFakeSignal() {
- auto mock_collector = std::make_unique<MockSignalsCollector>();
- ON_CALL(*mock_collector.get(), GetSupportedSignalNames())
- .WillByDefault(
- Return(std::unordered_set<std::string>({kOtherFakeSignalName})));
-
- ON_CALL(*mock_collector.get(), GetSignal(kOtherFakeSignalName, _, _))
- .WillByDefault(
- Invoke([](const std::string& signal_name, const base::Value& params,
- SignalsCollector::GetSignalCallback callback) {
- EXPECT_EQ(params, GetOtherFakeSignalParameter());
- std::move(callback).Run(GetOtherFakeSignalValue());
- }));
-
- return mock_collector;
-}
-
} // namespace
class SignalsAggregatorImplTest : public testing::Test {
protected:
SignalsAggregatorImplTest() {
- auto fake_signal_collector = GetCollectorForFakeSignal();
- fake_signal_collector_ = fake_signal_collector.get();
+ auto av_signal_collector = GetFakeCollector(SignalName::kAntiVirus);
+ av_signal_collector_ = av_signal_collector.get();
- auto other_fake_signal_collector = GetCollectorForOtherFakeSignal();
- other_fake_signal_collector_ = other_fake_signal_collector.get();
+ auto hotfix_signal_collector = GetFakeCollector(SignalName::kHotfixes);
+ hotfix_signal_collector_ = hotfix_signal_collector.get();
std::vector<std::unique_ptr<SignalsCollector>> collectors;
- collectors.push_back(std::move(fake_signal_collector));
- collectors.push_back(std::move(other_fake_signal_collector));
+ collectors.push_back(std::move(av_signal_collector));
+ collectors.push_back(std::move(hotfix_signal_collector));
aggregator_ = std::make_unique<SignalsAggregatorImpl>(
&mock_permission_service_, std::move(collectors));
}
@@ -114,72 +59,98 @@ class SignalsAggregatorImplTest : public testing::Test {
});
}
+ SignalsAggregationRequest CreateRequest() {
+ SignalsAggregationRequest request;
+ request.user_context = user_context_;
+ return request;
+ }
+
+ std::unique_ptr<MockSignalsCollector> GetFakeCollector(
+ SignalName signal_name) {
+ auto mock_collector = std::make_unique<MockSignalsCollector>();
+ ON_CALL(*mock_collector.get(), IsSignalSupported(_))
+ .WillByDefault(Return(false));
+ ON_CALL(*mock_collector.get(), IsSignalSupported(signal_name))
+ .WillByDefault(Return(true));
+
+ ON_CALL(*mock_collector.get(), GetSupportedSignalNames())
+ .WillByDefault(Return(std::unordered_set<SignalName>({signal_name})));
+
+ ON_CALL(*mock_collector.get(), GetSignal(signal_name, _, _, _))
+ .WillByDefault(Invoke([&](SignalName signal_name,
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) {
+ std::move(done_closure).Run();
+ }));
+
+ return mock_collector;
+ }
+
base::test::TaskEnvironment task_environment_;
- raw_ptr<MockSignalsCollector> fake_signal_collector_;
- raw_ptr<MockSignalsCollector> other_fake_signal_collector_;
+ raw_ptr<MockSignalsCollector> av_signal_collector_;
+ raw_ptr<MockSignalsCollector> hotfix_signal_collector_;
testing::StrictMock<MockUserPermissionService> mock_permission_service_;
UserContext user_context_{kGaiaId};
std::unique_ptr<SignalsAggregatorImpl> aggregator_;
+ base::HistogramTester histogram_tester_;
};
// Tests that the aggregator will return an empty value when given an empty
// parameter dictionary.
TEST_F(SignalsAggregatorImplTest, GetSignals_NoSignal) {
- base::test::TestFuture<base::Value> future;
- base::Value::Dict empty_value;
- aggregator_->GetSignals(user_context_, std::move(empty_value),
- future.GetCallback());
- EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
-}
-
-// Tests how the aggregator behaves when given a parameter with a single signal
-// which is supported by one of the collectors.
-TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_Supported) {
- GrantUserPermission();
-
- base::Value::Dict parameters;
- parameters.Set(kFakeSignalName, GetFakeSignalParameter());
-
- EXPECT_CALL(*fake_signal_collector_, GetSupportedSignalNames()).Times(1);
- EXPECT_CALL(*fake_signal_collector_, GetSignal(kFakeSignalName, _, _))
- .Times(1);
+ base::test::TestFuture<SignalsAggregationResponse> future;
+ aggregator_->GetSignals(std::move(CreateRequest()), future.GetCallback());
- EXPECT_CALL(*other_fake_signal_collector_, GetSignal(_, _, _)).Times(0);
+ SignalsAggregationResponse response = future.Get();
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(),
+ SignalCollectionError::kUnsupported);
+}
- base::test::TestFuture<base::Value> future;
- aggregator_->GetSignals(user_context_, std::move(parameters),
- future.GetCallback());
+// Tests that the aggregator will return an empty value when given a request
+// with multiple signal names.
+TEST_F(SignalsAggregatorImplTest, GetSignals_MultipleSignals) {
+ auto request = CreateRequest();
+ request.signal_names.emplace(SignalName::kAntiVirus);
+ request.signal_names.emplace(SignalName::kHotfixes);
- base::Value::Dict expected_value;
- expected_value.Set(kFakeSignalName, GetFakeSignalValue());
+ base::test::TestFuture<SignalsAggregationResponse> future;
+ aggregator_->GetSignals(request, future.GetCallback());
- EXPECT_EQ(future.Get(), base::Value(std::move(expected_value)));
+ SignalsAggregationResponse response = future.Get();
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(),
+ SignalCollectionError::kUnsupported);
}
// Tests how the aggregator behaves when given a parameter with a single signal
-// which is supported by another collector.
-TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_SupportedOther) {
+// which is supported by one of the collectors.
+TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_Supported) {
GrantUserPermission();
- base::Value::Dict parameters;
- parameters.Set(kOtherFakeSignalName, GetOtherFakeSignalParameter());
+ auto expected_signal_name = SignalName::kAntiVirus;
+ auto request = CreateRequest();
+ request.signal_names.emplace(expected_signal_name);
- EXPECT_CALL(*other_fake_signal_collector_, GetSupportedSignalNames())
+ EXPECT_CALL(*av_signal_collector_, IsSignalSupported(expected_signal_name))
.Times(1);
- EXPECT_CALL(*other_fake_signal_collector_,
- GetSignal(kOtherFakeSignalName, _, _))
+ EXPECT_CALL(*av_signal_collector_,
+ GetSignal(SignalName::kAntiVirus, request, _, _))
.Times(1);
- EXPECT_CALL(*fake_signal_collector_, GetSignal(_, _, _)).Times(0);
+ EXPECT_CALL(*hotfix_signal_collector_, GetSignal(_, _, _, _)).Times(0);
- base::test::TestFuture<base::Value> future;
- aggregator_->GetSignals(user_context_, std::move(parameters),
- future.GetCallback());
+ base::test::TestFuture<SignalsAggregationResponse> future;
+ aggregator_->GetSignals(std::move(request), future.GetCallback());
- base::Value::Dict expected_value;
- expected_value.Set(kOtherFakeSignalName, GetOtherFakeSignalValue());
+ SignalsAggregationResponse response = future.Get();
+ EXPECT_FALSE(response.top_level_error.has_value());
- EXPECT_EQ(future.Get(), base::Value(std::move(expected_value)));
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.DeviceSignals.Collection.Request", expected_signal_name, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.DeviceSignals.UserPermission", UserPermission::kGranted, 1);
}
// Tests how the aggregator behaves when given a parameter with a single signal
@@ -187,25 +158,39 @@ TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_SupportedOther) {
TEST_F(SignalsAggregatorImplTest, GetSignals_SingleSignal_Unsupported) {
GrantUserPermission();
- base::Value::Dict parameters;
- parameters.Set("something unsupported", base::Value());
+ auto expected_signal_name = SignalName::kFileSystemInfo;
+ auto request = CreateRequest();
+ request.signal_names.emplace(expected_signal_name);
+
+ base::test::TestFuture<SignalsAggregationResponse> future;
+ aggregator_->GetSignals(std::move(request), future.GetCallback());
- base::test::TestFuture<base::Value> future;
- aggregator_->GetSignals(user_context_, std::move(parameters),
- future.GetCallback());
- EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
+ SignalsAggregationResponse response = future.Get();
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(),
+ SignalCollectionError::kUnsupported);
+
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.DeviceSignals.Collection.Request", expected_signal_name, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.DeviceSignals.UserPermission", UserPermission::kGranted, 1);
}
// Tests how the aggregator behaves when encountering user permission errors.
TEST_F(SignalsAggregatorImplTest, GetSignals_InvalidUserPermissions) {
- std::map<UserPermission, std::string> permission_to_error_map;
+ std::map<UserPermission, SignalCollectionError> permission_to_error_map;
permission_to_error_map[UserPermission::kUnaffiliated] =
- errors::kUnaffiliatedUser;
+ SignalCollectionError::kUnaffiliatedUser;
permission_to_error_map[UserPermission::kMissingConsent] =
- errors::kConsentRequired;
- permission_to_error_map[UserPermission::kConsumerUser] = errors::kUnsupported;
- permission_to_error_map[UserPermission::kUnknownUser] = errors::kUnsupported;
-
+ SignalCollectionError::kConsentRequired;
+ permission_to_error_map[UserPermission::kConsumerUser] =
+ SignalCollectionError::kInvalidUser;
+ permission_to_error_map[UserPermission::kUnknownUser] =
+ SignalCollectionError::kInvalidUser;
+ permission_to_error_map[UserPermission::kMissingUser] =
+ SignalCollectionError::kInvalidUser;
+
+ uint16_t item_index = 0;
for (const auto& test_case : permission_to_error_map) {
EXPECT_CALL(mock_permission_service_, CanCollectSignals(user_context_, _))
.WillOnce(
@@ -215,14 +200,21 @@ TEST_F(SignalsAggregatorImplTest, GetSignals_InvalidUserPermissions) {
});
// This value is not important for these test cases.
- base::Value::Dict parameters;
- parameters.Set("something unsupported", base::Value());
-
- base::test::TestFuture<base::Value> future;
- aggregator_->GetSignals(user_context_, std::move(parameters),
- future.GetCallback());
-
- EXPECT_EQ(future.Get(), base::Value(test_case.second));
+ auto expected_signal_name = SignalName::kAntiVirus;
+ auto request = CreateRequest();
+ request.signal_names.emplace(expected_signal_name);
+
+ base::test::TestFuture<SignalsAggregationResponse> future;
+ aggregator_->GetSignals(std::move(request), future.GetCallback());
+ SignalsAggregationResponse response = future.Get();
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(), test_case.second);
+
+ histogram_tester_.ExpectUniqueSample(
+ "Enterprise.DeviceSignals.Collection.Request", expected_signal_name,
+ ++item_index);
+ histogram_tester_.ExpectBucketCount(
+ "Enterprise.DeviceSignals.UserPermission", test_case.first, 1);
}
}
diff --git a/chromium/components/device_signals/core/browser/signals_collector.h b/chromium/components/device_signals/core/browser/signals_collector.h
index 9c63ae5a151..95712a2e0ee 100644
--- a/chromium/components/device_signals/core/browser/signals_collector.h
+++ b/chromium/components/device_signals/core/browser/signals_collector.h
@@ -5,31 +5,36 @@
#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_COLLECTOR_H_
#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_COLLECTOR_H_
-#include <string>
#include <unordered_set>
#include "base/callback_forward.h"
-namespace base {
-class Value;
-} // namespace base
-
namespace device_signals {
+enum class SignalName;
+struct SignalsAggregationRequest;
+struct SignalsAggregationResponse;
+
class SignalsCollector {
public:
- using GetSignalCallback = base::OnceCallback<void(base::Value)>;
-
virtual ~SignalsCollector() = default;
- // Returns the set of signal names that this collector can collect.
- virtual const std::unordered_set<std::string> GetSupportedSignalNames() = 0;
+ // Returns true if `signal_name` is part of the set of signals supported by
+ // this collector.
+ virtual bool IsSignalSupported(SignalName signal_name) = 0;
- // Collects the signal named `signal_name` using `params` (if needed), and
- // invokes `callback` with the signal value.
- virtual void GetSignal(const std::string& signal_name,
- const base::Value& params,
- GetSignalCallback callback) = 0;
+ // Returns the set of signal names that this collector can collect.
+ virtual const std::unordered_set<SignalName> GetSupportedSignalNames() = 0;
+
+ // Collects the signal named `signal_name` using parameters in `request`
+ // (if needed), sets the collected values on `response` and invokes
+ // `done_closure` when the signal is collected. `response` is owned by the
+ // caller who is responsible for keeping the value alive while the signal is
+ // being collected.
+ virtual void GetSignal(SignalName signal_name,
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) = 0;
};
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/signals_types.cc b/chromium/components/device_signals/core/browser/signals_types.cc
new file mode 100644
index 00000000000..c92f4f8943f
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_types.cc
@@ -0,0 +1,85 @@
+// 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 "components/device_signals/core/browser/signals_types.h"
+
+#include "components/device_signals/core/common/signals_constants.h"
+
+namespace device_signals {
+
+const std::string ErrorToString(SignalCollectionError error) {
+ switch (error) {
+ case SignalCollectionError::kConsentRequired:
+ return errors::kConsentRequired;
+ case SignalCollectionError::kUnaffiliatedUser:
+ return errors::kUnaffiliatedUser;
+ case SignalCollectionError::kUnsupported:
+ return errors::kUnsupported;
+ case SignalCollectionError::kMissingSystemService:
+ return errors::kMissingSystemService;
+ case SignalCollectionError::kMissingBundle:
+ return errors::kMissingBundle;
+ case SignalCollectionError::kInvalidUser:
+ return errors::kInvalidUser;
+ case SignalCollectionError::kMissingParameters:
+ return errors::kMissingParameters;
+ }
+}
+
+BaseSignalResponse::~BaseSignalResponse() = default;
+
+#if BUILDFLAG(IS_WIN)
+AntiVirusSignalResponse::AntiVirusSignalResponse() = default;
+AntiVirusSignalResponse::AntiVirusSignalResponse(
+ const AntiVirusSignalResponse&) = default;
+
+AntiVirusSignalResponse& AntiVirusSignalResponse::operator=(
+ const AntiVirusSignalResponse&) = default;
+
+AntiVirusSignalResponse::~AntiVirusSignalResponse() = default;
+
+HotfixSignalResponse::HotfixSignalResponse() = default;
+HotfixSignalResponse::HotfixSignalResponse(const HotfixSignalResponse&) =
+ default;
+
+HotfixSignalResponse& HotfixSignalResponse::operator=(
+ const HotfixSignalResponse&) = default;
+
+HotfixSignalResponse::~HotfixSignalResponse() = default;
+#endif // BUILDFLAG(IS_WIN)
+
+FileSystemInfoResponse::FileSystemInfoResponse() = default;
+FileSystemInfoResponse::FileSystemInfoResponse(const FileSystemInfoResponse&) =
+ default;
+
+FileSystemInfoResponse& FileSystemInfoResponse::operator=(
+ const FileSystemInfoResponse&) = default;
+
+FileSystemInfoResponse::~FileSystemInfoResponse() = default;
+
+SignalsAggregationRequest::SignalsAggregationRequest() = default;
+SignalsAggregationRequest::SignalsAggregationRequest(
+ const SignalsAggregationRequest&) = default;
+
+SignalsAggregationRequest& SignalsAggregationRequest::operator=(
+ const SignalsAggregationRequest&) = default;
+
+SignalsAggregationRequest::~SignalsAggregationRequest() = default;
+
+bool SignalsAggregationRequest::operator==(
+ const SignalsAggregationRequest& other) const {
+ return user_context == other.user_context &&
+ signal_names == other.signal_names;
+}
+
+SignalsAggregationResponse::SignalsAggregationResponse() = default;
+SignalsAggregationResponse::SignalsAggregationResponse(
+ const SignalsAggregationResponse&) = default;
+
+SignalsAggregationResponse& SignalsAggregationResponse::operator=(
+ const SignalsAggregationResponse&) = default;
+
+SignalsAggregationResponse::~SignalsAggregationResponse() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/signals_types.h b/chromium/components/device_signals/core/browser/signals_types.h
new file mode 100644
index 00000000000..ae7792f4e6e
--- /dev/null
+++ b/chromium/components/device_signals/core/browser/signals_types.h
@@ -0,0 +1,150 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_TYPES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_TYPES_H_
+
+#include <unordered_set>
+#include <vector>
+
+#include "build/build_config.h"
+#include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "components/device_signals/core/common/win/win_types.h"
+#endif // BUILDFLAG(IS_WIN)
+
+namespace device_signals {
+
+// Enum of names representing signals bundles that can be aggregated via the
+// SignalsAggregator.
+// These values are persisted to logs and should not be renumbered. Please
+// update the DeviceSignalsSignalName enum in enums.xml when adding a new
+// value here.
+enum class SignalName {
+ kAntiVirus,
+ kHotfixes,
+ kFileSystemInfo,
+ kSystemSettings,
+ kMaxValue = kSystemSettings
+};
+
+// Superset of all signal collection errors that can occur, including top-level
+// as well as per-bundle errors.
+// These values are persisted to logs and should not be renumbered. Please
+// update the DeviceSignalsSignalCollectionError enum in enums.xml when adding a
+// new value here.
+enum class SignalCollectionError {
+ kConsentRequired,
+ kUnaffiliatedUser,
+ kUnsupported,
+ kMissingSystemService,
+ kMissingBundle,
+ kInvalidUser,
+ kMissingParameters,
+ kMaxValue = kMissingParameters
+};
+
+const std::string ErrorToString(SignalCollectionError error);
+
+// Base struct type that each specific signal bundle types should extend. The
+// derived signal bundles/responses should group a set of signals that
+// cohesively belong together (e.g. device-level signals, policy values
+// signals).
+struct BaseSignalResponse {
+ virtual ~BaseSignalResponse();
+
+ // If set, represents a collection error that occurred while getting the
+ // signal.
+ absl::optional<SignalCollectionError> collection_error = absl::nullopt;
+};
+
+#if BUILDFLAG(IS_WIN)
+struct AntiVirusSignalResponse : BaseSignalResponse {
+ AntiVirusSignalResponse();
+
+ AntiVirusSignalResponse(const AntiVirusSignalResponse&);
+ AntiVirusSignalResponse& operator=(const AntiVirusSignalResponse&);
+
+ ~AntiVirusSignalResponse() override;
+
+ std::vector<AvProduct> av_products{};
+};
+
+struct HotfixSignalResponse : BaseSignalResponse {
+ HotfixSignalResponse();
+
+ HotfixSignalResponse(const HotfixSignalResponse&);
+ HotfixSignalResponse& operator=(const HotfixSignalResponse&);
+
+ ~HotfixSignalResponse() override;
+
+ std::vector<InstalledHotfix> hotfixes{};
+};
+#endif // BUILDFLAG(IS_WIN)
+
+struct FileSystemInfoResponse : BaseSignalResponse {
+ FileSystemInfoResponse();
+
+ FileSystemInfoResponse(const FileSystemInfoResponse&);
+ FileSystemInfoResponse& operator=(const FileSystemInfoResponse&);
+
+ ~FileSystemInfoResponse() override;
+
+ std::vector<FileSystemItem> file_system_items{};
+};
+
+// Request struct containing properties that will be used by the
+// SignalAggregator to validate signals access permissions while delegating
+// the collection to the right Collectors. Signals that require parameters (e.g.
+// FileSystemInfo) will look for them in this object.
+struct SignalsAggregationRequest {
+ SignalsAggregationRequest();
+
+ SignalsAggregationRequest(const SignalsAggregationRequest&);
+ SignalsAggregationRequest& operator=(const SignalsAggregationRequest&);
+
+ ~SignalsAggregationRequest();
+
+ // Information about the user for whom these signals are collected.
+ UserContext user_context{};
+
+ // Names of the signals that need to be collected.
+ std::unordered_set<SignalName> signal_names{};
+
+ // Parameters required when requesting the collection of signals living on
+ // the device's file system.
+ std::vector<GetFileSystemInfoOptions> file_system_signal_parameters{};
+
+ bool operator==(const SignalsAggregationRequest& other) const;
+};
+
+// Response from a signal collection request sent through the SignalsAggregator.
+// The signal bundles on this object will be set according to the set of signal
+// names given in the corresponding `SignalsAggregationRequest`.
+struct SignalsAggregationResponse {
+ SignalsAggregationResponse();
+
+ SignalsAggregationResponse(const SignalsAggregationResponse&);
+ SignalsAggregationResponse& operator=(const SignalsAggregationResponse&);
+
+ ~SignalsAggregationResponse();
+
+ // If set, represents an error that occurred before any signal could be
+ // collected.
+ absl::optional<SignalCollectionError> top_level_error = absl::nullopt;
+
+#if BUILDFLAG(IS_WIN)
+ absl::optional<AntiVirusSignalResponse> av_signal_response = absl::nullopt;
+ absl::optional<HotfixSignalResponse> hotfix_signal_response = absl::nullopt;
+#endif // BUILDFLAG(IS_WIN)
+ absl::optional<FileSystemInfoResponse> file_system_info_response =
+ absl::nullopt;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_SIGNALS_TYPES_H_
diff --git a/chromium/components/device_signals/core/browser/user_context.cc b/chromium/components/device_signals/core/browser/user_context.cc
index ed3d091b247..8a74ea46842 100644
--- a/chromium/components/device_signals/core/browser/user_context.cc
+++ b/chromium/components/device_signals/core/browser/user_context.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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.
diff --git a/chromium/components/device_signals/core/browser/user_delegate.h b/chromium/components/device_signals/core/browser/user_delegate.h
index a98f430be15..bfe1c554536 100644
--- a/chromium/components/device_signals/core/browser/user_delegate.h
+++ b/chromium/components/device_signals/core/browser/user_delegate.h
@@ -5,23 +5,26 @@
#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_DELEGATE_H_
#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_USER_DELEGATE_H_
-struct AccountInfo;
+#include <string>
namespace device_signals {
-// Delegate representing the user that is currently logged into the browser
+// Delegate representing the user that is currently logged-in to the browser
// itself.
class UserDelegate {
public:
virtual ~UserDelegate() = default;
- // Returns true if the current user is managed by an organization that is
- // affiliated with the organization managing the device.
+ // Returns true if the current browser user is managed by an organization that
+ // is affiliated with the organization managing the device.
virtual bool IsAffiliated() const = 0;
- // Returns true if the user currently logged into the browser is managed and
- // is the same user as `user`.
- virtual bool IsSameManagedUser(const AccountInfo& user) const = 0;
+ // Returns true if the current browser user is managed.
+ virtual bool IsManaged() const = 0;
+
+ // Returns true if `gaia_id` represents the same user as the one currently
+ // logged-in to the browser.
+ virtual bool IsSameUser(const std::string& gaia_id) const = 0;
};
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/user_permission_service.h b/chromium/components/device_signals/core/browser/user_permission_service.h
index e82c95cf34a..2db08b4c77a 100644
--- a/chromium/components/device_signals/core/browser/user_permission_service.h
+++ b/chromium/components/device_signals/core/browser/user_permission_service.h
@@ -12,6 +12,10 @@ namespace device_signals {
struct UserContext;
+// Contains possible outcomes of a signals collection permission check.
+// These values are persisted to logs and should not be renumbered. Please
+// update the DeviceSignalsUserPermission enum in enums.xml when adding a new
+// value here.
enum class UserPermission {
// Returned when the user is part of an organization that is not affiliated
// with the organization currently managing the browser.
@@ -24,12 +28,17 @@ enum class UserPermission {
// Returned when the user is not part of any organization.
kConsumerUser = 2,
- // Returned when the given user context does not represent any currently
- // logged-in user.
+ // Returned when the given user context does not represent the current browser
+ // user (e.g. Profile user).
kUnknownUser = 3,
+ // Returned when the no user information was given.
+ kMissingUser = 4,
+
// Returned when the user is granted permission to the device's signals.
- kGranted = 4,
+ kGranted = 5,
+
+ kMaxValue = kGranted
};
// Service that can be used to conduct permission checks on given users. The
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl.cc b/chromium/components/device_signals/core/browser/user_permission_service_impl.cc
index c2ac146f9ff..9e18c292e15 100644
--- a/chromium/components/device_signals/core/browser/user_permission_service_impl.cc
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl.cc
@@ -8,18 +8,14 @@
#include "components/device_signals/core/browser/user_context.h"
#include "components/device_signals/core/browser/user_delegate.h"
#include "components/policy/core/common/management/management_service.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
namespace device_signals {
UserPermissionServiceImpl::UserPermissionServiceImpl(
- signin::IdentityManager* identity_manager,
policy::ManagementService* management_service,
std::unique_ptr<UserDelegate> user_delegate)
- : identity_manager_(identity_manager),
- management_service_(management_service),
+ : management_service_(management_service),
user_delegate_(std::move(user_delegate)) {
- DCHECK(identity_manager_);
DCHECK(management_service_);
DCHECK(user_delegate_);
}
@@ -29,24 +25,18 @@ UserPermissionServiceImpl::~UserPermissionServiceImpl() = default;
void UserPermissionServiceImpl::CanCollectSignals(
const UserContext& user_context,
UserPermissionService::CanCollectCallback callback) {
- // Return "unknown user" if the user ID is invalid, or does not represent a
- // logged-in user.
+ // Return "unknown user" if no user ID was given.
if (user_context.user_id.empty()) {
- std::move(callback).Run(UserPermission::kUnknownUser);
+ std::move(callback).Run(UserPermission::kMissingUser);
return;
}
- // TODO(b:233250828): Verify this function covers the required use cases.
- auto account_info =
- identity_manager_->FindExtendedAccountInfoByGaiaId(user_context.user_id);
-
- if (account_info.IsEmpty()) {
+ if (!user_delegate_->IsSameUser(user_context.user_id)) {
std::move(callback).Run(UserPermission::kUnknownUser);
return;
}
- // Return "consumer user" if the user is not managed by an organization.
- if (!account_info.IsManaged()) {
+ if (!user_delegate_->IsManaged()) {
std::move(callback).Run(UserPermission::kConsumerUser);
return;
}
@@ -61,20 +51,16 @@ void UserPermissionServiceImpl::CanCollectSignals(
return;
}
- // If the account info represents the current browser user (e.g. Profile
- // user), verify if that user is part of an affiliated organization.
- if (user_delegate_->IsSameManagedUser(account_info)) {
- UserPermission permission = user_delegate_->IsAffiliated()
- ? UserPermission::kGranted
- : UserPermission::kUnaffiliated;
- std::move(callback).Run(permission);
+ if (!user_delegate_->IsAffiliated()) {
+ std::move(callback).Run(UserPermission::kUnaffiliated);
return;
}
- // TODO(b:232269863): Fetch customer IDs for that user by calling the DM
- // server, then cache results here (and clear cache when the account is
- // logged-out).
- std::move(callback).Run(UserPermission::kUnaffiliated);
+ // At this point, the given user is:
+ // - The same user as the browser user,
+ // - Is managed by an org affiliated with the org managing the browser.
+ // They are, therefore, allowed to collect signals.
+ std::move(callback).Run(UserPermission::kGranted);
}
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl.h b/chromium/components/device_signals/core/browser/user_permission_service_impl.h
index 3e77b195987..aa29d32fef0 100644
--- a/chromium/components/device_signals/core/browser/user_permission_service_impl.h
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl.h
@@ -15,18 +15,13 @@ namespace policy {
class ManagementService;
} // namespace policy
-namespace signin {
-class IdentityManager;
-} // namespace signin
-
namespace device_signals {
class UserDelegate;
class UserPermissionServiceImpl : public UserPermissionService {
public:
- UserPermissionServiceImpl(signin::IdentityManager* identity_manager,
- policy::ManagementService* management_service,
+ UserPermissionServiceImpl(policy::ManagementService* management_service,
std::unique_ptr<UserDelegate> user_delegate);
UserPermissionServiceImpl(const UserPermissionServiceImpl&) = delete;
@@ -40,7 +35,6 @@ class UserPermissionServiceImpl : public UserPermissionService {
CanCollectCallback callback) override;
private:
- base::raw_ptr<signin::IdentityManager> identity_manager_;
base::raw_ptr<policy::ManagementService> management_service_;
std::unique_ptr<UserDelegate> user_delegate_;
diff --git a/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc b/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc
index d6579051c74..f70cadc6f93 100644
--- a/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc
+++ b/chromium/components/device_signals/core/browser/user_permission_service_impl_unittest.cc
@@ -12,8 +12,6 @@
#include "components/device_signals/core/browser/user_delegate.h"
#include "components/policy/core/common/management/management_service.h"
#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,8 +25,6 @@ namespace device_signals {
namespace {
constexpr char kUserGaiaId[] = "some-gaia-id";
-constexpr char kUserEmail[] = "user@abc.com";
-constexpr char kHostedDomain[] = "example.com";
class TestManagementService : public policy::ManagementService {
public:
@@ -55,13 +51,11 @@ class UserPermissionServiceImplTest : public testing::Test {
mock_user_delegate_ = mock_user_delegate.get();
permission_service_ = std::make_unique<UserPermissionServiceImpl>(
- identity_test_env_.identity_manager(), &management_service_,
- std::move(mock_user_delegate));
+ &management_service_, std::move(mock_user_delegate));
}
base::test::TaskEnvironment task_environment_;
- signin::IdentityTestEnvironment identity_test_env_;
TestManagementService management_service_;
ScopedManagementServiceOverrideForTesting scoped_override_;
raw_ptr<testing::StrictMock<MockUserDelegate>> mock_user_delegate_;
@@ -74,32 +68,35 @@ TEST_F(UserPermissionServiceImplTest, CanCollectSignals_EmptyUserId) {
base::test::TestFuture<UserPermission> future;
UserContext user_context;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
- EXPECT_EQ(future.Get(), UserPermission::kUnknownUser);
+ EXPECT_EQ(future.Get(), UserPermission::kMissingUser);
}
-// Tests CanCollectSignals with a user ID that does not represent a valid user.
-TEST_F(UserPermissionServiceImplTest, CanCollectSignals_UserId_NoUser) {
- base::test::TestFuture<UserPermission> future;
+// Tests CanCollectSignals with a user ID that does not represent the current
+// browser user.
+TEST_F(UserPermissionServiceImplTest, CanCollectSignals_UserId_NotSameUser) {
UserContext user_context;
user_context.user_id = kUserGaiaId;
+
+ // Mock that it is not the same user.
+ EXPECT_CALL(*mock_user_delegate_, IsSameUser(kUserGaiaId))
+ .WillOnce(Return(false));
+
+ base::test::TestFuture<UserPermission> future;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
EXPECT_EQ(future.Get(), UserPermission::kUnknownUser);
}
-// Tests CanCollectSignals with a user ID that does not represent a managed
-// user.
+// Tests CanCollectSignals with a user ID that represents the browser user, but
+// that user is not managed.
TEST_F(UserPermissionServiceImplTest, CanCollectSignals_User_NotManaged) {
- // Create known account.
- AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
- kUserEmail, kUserGaiaId);
+ UserContext user_context;
+ user_context.user_id = kUserGaiaId;
- // Make sure there is no hosted domain.
- account.hosted_domain = "";
- identity_test_env_.UpdateAccountInfoForAccount(account);
+ EXPECT_CALL(*mock_user_delegate_, IsSameUser(kUserGaiaId))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsManaged()).WillOnce(Return(false));
base::test::TestFuture<UserPermission> future;
- UserContext user_context;
- user_context.user_id = account.gaia;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
EXPECT_EQ(future.Get(), UserPermission::kConsumerUser);
}
@@ -111,17 +108,14 @@ TEST_F(UserPermissionServiceImplTest, CanCollectSignals_BrowserNotManaged) {
ScopedManagementServiceOverrideForTesting another_scope(
&management_service_, EnterpriseManagementAuthority::CLOUD);
- // Create known account.
- AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
- kUserEmail, kUserGaiaId);
+ UserContext user_context;
+ user_context.user_id = kUserGaiaId;
- // Make sure there is a hosted domain.
- account.hosted_domain = kHostedDomain;
- identity_test_env_.UpdateAccountInfoForAccount(account);
+ EXPECT_CALL(*mock_user_delegate_, IsSameUser(kUserGaiaId))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsManaged()).WillOnce(Return(true));
base::test::TestFuture<UserPermission> future;
- UserContext user_context;
- user_context.user_id = account.gaia;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
EXPECT_EQ(future.Get(), UserPermission::kMissingConsent);
}
@@ -131,21 +125,15 @@ TEST_F(UserPermissionServiceImplTest, CanCollectSignals_BrowserNotManaged) {
// the browser's org.
TEST_F(UserPermissionServiceImplTest,
CanCollectSignals_BrowserManaged_ProfileUser_Unaffiliated) {
- // Create known account.
- AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
- kUserEmail, kUserGaiaId);
-
- // Make sure there is a hosted domain.
- account.hosted_domain = kHostedDomain;
- identity_test_env_.UpdateAccountInfoForAccount(account);
+ UserContext user_context;
+ user_context.user_id = kUserGaiaId;
- EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
+ EXPECT_CALL(*mock_user_delegate_, IsSameUser(kUserGaiaId))
.WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsManaged()).WillOnce(Return(true));
EXPECT_CALL(*mock_user_delegate_, IsAffiliated()).WillOnce(Return(false));
base::test::TestFuture<UserPermission> future;
- UserContext user_context;
- user_context.user_id = account.gaia;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
EXPECT_EQ(future.Get(), UserPermission::kUnaffiliated);
}
@@ -155,47 +143,17 @@ TEST_F(UserPermissionServiceImplTest,
// browser's org.
TEST_F(UserPermissionServiceImplTest,
CanCollectSignals_BrowserManaged_ProfileUser_Affiliated) {
- // Create known account.
- AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
- kUserEmail, kUserGaiaId);
-
- // Make sure there is a hosted domain.
- account.hosted_domain = kHostedDomain;
- identity_test_env_.UpdateAccountInfoForAccount(account);
+ UserContext user_context;
+ user_context.user_id = kUserGaiaId;
- EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
+ EXPECT_CALL(*mock_user_delegate_, IsSameUser(kUserGaiaId))
.WillOnce(Return(true));
+ EXPECT_CALL(*mock_user_delegate_, IsManaged()).WillOnce(Return(true));
EXPECT_CALL(*mock_user_delegate_, IsAffiliated()).WillOnce(Return(true));
base::test::TestFuture<UserPermission> future;
- UserContext user_context;
- user_context.user_id = account.gaia;
permission_service_->CanCollectSignals(user_context, future.GetCallback());
EXPECT_EQ(future.Get(), UserPermission::kGranted);
}
-// Tests CanCollectSignals with a managed user ID and the browser is managed,
-// but the user is not the Profile user.
-// This is missing the remote affiliation check at the moment an defaults to
-// "unaffiliated".
-TEST_F(UserPermissionServiceImplTest,
- CanCollectSignals_BrowserManaged_NotProfile) {
- // Create known account.
- AccountInfo account = identity_test_env_.MakeAccountAvailableWithCookies(
- kUserEmail, kUserGaiaId);
-
- // Make sure there is a hosted domain.
- account.hosted_domain = kHostedDomain;
- identity_test_env_.UpdateAccountInfoForAccount(account);
-
- EXPECT_CALL(*mock_user_delegate_, IsSameManagedUser(account))
- .WillOnce(Return(false));
-
- base::test::TestFuture<UserPermission> future;
- UserContext user_context;
- user_context.user_id = account.gaia;
- permission_service_->CanCollectSignals(user_context, future.GetCallback());
- EXPECT_EQ(future.Get(), UserPermission::kUnaffiliated);
-}
-
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector.cc b/chromium/components/device_signals/core/browser/win/win_signals_collector.cc
index 67ce37a9476..f6951ac4822 100644
--- a/chromium/components/device_signals/core/browser/win/win_signals_collector.cc
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector.cc
@@ -4,99 +4,92 @@
#include "components/device_signals/core/browser/win/win_signals_collector.h"
+#include <functional>
+
#include "base/bind.h"
#include "base/check.h"
-#include "base/values.h"
+#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/browser/system_signals_service_host.h"
#include "components/device_signals/core/common/mojom/system_signals.mojom.h"
-#include "components/device_signals/core/common/signals_constants.h"
+#include "components/device_signals/core/common/win/win_types.h"
namespace device_signals {
WinSignalsCollector::WinSignalsCollector(
SystemSignalsServiceHost* system_service_host)
- : system_service_host_(system_service_host),
- signals_collection_map_({
- {names::kAntiVirusInfo,
+ : BaseSignalsCollector({
+ {SignalName::kAntiVirus,
base::BindRepeating(&WinSignalsCollector::GetAntiVirusSignal,
base::Unretained(this))},
- {names::kInstalledHotfixes,
+ {SignalName::kHotfixes,
base::BindRepeating(&WinSignalsCollector::GetHotfixSignal,
base::Unretained(this))},
- }) {
+ }),
+ system_service_host_(system_service_host) {
DCHECK(system_service_host_);
}
WinSignalsCollector::~WinSignalsCollector() = default;
-const std::unordered_set<std::string>
-WinSignalsCollector::GetSupportedSignalNames() {
- std::unordered_set<std::string> supported_signals;
- for (const auto& collection_pair : signals_collection_map_) {
- supported_signals.insert(collection_pair.first);
- }
-
- return supported_signals;
-}
-
-void WinSignalsCollector::GetSignal(const std::string& signal_name,
- const base::Value& params,
- GetSignalCallback callback) {
- const auto it = signals_collection_map_.find(signal_name);
- if (it == signals_collection_map_.end()) {
- std::move(callback).Run(base::Value(errors::kUnsupported));
- return;
- }
-
- it->second.Run(params, std::move(callback));
-}
-
-void WinSignalsCollector::GetAntiVirusSignal(const base::Value& params,
- GetSignalCallback callback) {
+void WinSignalsCollector::GetAntiVirusSignal(
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) {
auto* system_signals_service = system_service_host_->GetService();
if (!system_signals_service) {
- std::move(callback).Run(base::Value(errors::kMissingSystemService));
+ AntiVirusSignalResponse av_response;
+ av_response.collection_error = SignalCollectionError::kMissingSystemService;
+ response.av_signal_response = std::move(av_response);
+ std::move(done_closure).Run();
return;
}
- system_signals_service->GetAntiVirusSignals(
- base::BindOnce(&WinSignalsCollector::OnAntiVirusSignalCollected,
- weak_factory_.GetWeakPtr(), std::move(callback)));
+ system_signals_service->GetAntiVirusSignals(base::BindOnce(
+ &WinSignalsCollector::OnAntiVirusSignalCollected,
+ weak_factory_.GetWeakPtr(), std::ref(response), std::move(done_closure)));
}
void WinSignalsCollector::OnAntiVirusSignalCollected(
- GetSignalCallback callback,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
const std::vector<AvProduct>& av_products) {
- base::Value::List av_values;
- for (const auto& av_product : av_products) {
- av_values.Append(av_product.ToValue());
- }
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ AntiVirusSignalResponse av_response;
+ av_response.av_products = std::move(av_products);
+ response.av_signal_response = std::move(av_response);
- std::move(callback).Run(base::Value(std::move(av_values)));
+ std::move(done_closure).Run();
}
-void WinSignalsCollector::GetHotfixSignal(const base::Value& params,
- GetSignalCallback callback) {
+void WinSignalsCollector::GetHotfixSignal(
+ const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure) {
auto* system_signals_service = system_service_host_->GetService();
if (!system_signals_service) {
- std::move(callback).Run(base::Value(errors::kMissingSystemService));
+ HotfixSignalResponse hotfix_response;
+ hotfix_response.collection_error =
+ SignalCollectionError::kMissingSystemService;
+ response.hotfix_signal_response = std::move(hotfix_response);
+ std::move(done_closure).Run();
return;
}
- system_signals_service->GetHotfixSignals(
- base::BindOnce(&WinSignalsCollector::OnHotfixSignalCollected,
- weak_factory_.GetWeakPtr(), std::move(callback)));
+ system_signals_service->GetHotfixSignals(base::BindOnce(
+ &WinSignalsCollector::OnHotfixSignalCollected, weak_factory_.GetWeakPtr(),
+ std::ref(response), std::move(done_closure)));
}
void WinSignalsCollector::OnHotfixSignalCollected(
- GetSignalCallback callback,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
const std::vector<InstalledHotfix>& hotfixes) {
- base::Value::List hotfix_values;
- for (const auto& hotfix : hotfixes) {
- hotfix_values.Append(hotfix.ToValue());
- }
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ HotfixSignalResponse hotfix_response;
+ hotfix_response.hotfixes = std::move(hotfixes);
+ response.hotfix_signal_response = std::move(hotfix_response);
- std::move(callback).Run(base::Value(std::move(hotfix_values)));
+ std::move(done_closure).Run();
}
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector.h b/chromium/components/device_signals/core/browser/win/win_signals_collector.h
index c80f6e8be94..d88d766103b 100644
--- a/chromium/components/device_signals/core/browser/win/win_signals_collector.h
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector.h
@@ -5,24 +5,21 @@
#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_WIN_WIN_SIGNALS_COLLECTOR_H_
#define COMPONENTS_DEVICE_SIGNALS_CORE_BROWSER_WIN_WIN_SIGNALS_COLLECTOR_H_
-#include <map>
#include <vector>
#include "base/memory/weak_ptr.h"
-#include "components/device_signals/core/browser/signals_collector.h"
-#include "components/device_signals/core/common/win/win_types.h"
-
-namespace base {
-class Value;
-} // namespace base
+#include "base/sequence_checker.h"
+#include "components/device_signals/core/browser/base_signals_collector.h"
namespace device_signals {
+struct AvProduct;
+struct InstalledHotfix;
class SystemSignalsServiceHost;
// Collector in charge of collecting device signals that are either specific to
// Windows, or require a specific Windows-only implementation.
-class WinSignalsCollector : public SignalsCollector {
+class WinSignalsCollector : public BaseSignalsCollector {
public:
explicit WinSignalsCollector(SystemSignalsServiceHost* system_service_host);
@@ -31,45 +28,43 @@ class WinSignalsCollector : public SignalsCollector {
WinSignalsCollector(const WinSignalsCollector&) = delete;
WinSignalsCollector& operator=(const WinSignalsCollector&) = delete;
- // SignalsCollector:
- const std::unordered_set<std::string> GetSupportedSignalNames() override;
- void GetSignal(const std::string& signal_name,
- const base::Value& params,
- GetSignalCallback callback) override;
-
private:
- // Collection function for the AV signal. `params` is ignored since AV signal
- // does not require parameters. `callback` will be invoked when the signal
- // values are available, or when something went wrong.
- void GetAntiVirusSignal(const base::Value& params,
- GetSignalCallback callback);
+ // Collection function for the Antivirus signal. `request` is ignored since AV
+ // signal does not require parameters. `response` will be passed along and the
+ // signal values will be set on it when available. `done_closure` will be
+ // invoked when signal collection is complete.
+ void GetAntiVirusSignal(const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure);
// Invoked when the SystemSignalsService returns the collected AV signals as
- // `av_products`. Will convert the structs to base::Value and invoke
- // `callback`.
- void OnAntiVirusSignalCollected(GetSignalCallback callback,
+ // `av_products`. Will update `response` with the signal collection outcome,
+ // and invoke `done_closure` to asynchronously notify the caller of the
+ // completion of this request.
+ void OnAntiVirusSignalCollected(SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
const std::vector<AvProduct>& av_products);
- // Collection function for the Hotfix signal. `params` is ignored since Hotfix
- // signal does not require parameters. `callback` will be invoked when the
- // signal values are available, or when something went wrong.
- void GetHotfixSignal(const base::Value& params, GetSignalCallback callback);
+ // Collection function for the Hotfix signal. `request` is ignored since
+ // Hotfix signal does not require parameters. `response` will be passed along
+ // and the signal values will be set on it when available. `done_closure` will
+ // be invoked when signal collection is complete.
+ void GetHotfixSignal(const SignalsAggregationRequest& request,
+ SignalsAggregationResponse& response,
+ base::OnceClosure done_closure);
// Invoked when the SystemSignalsService returns the collected Hotfix signals
- // as `hotfixes`. Will convert the structs to base::Value and invoke
- // `callback`.
- void OnHotfixSignalCollected(GetSignalCallback callback,
+ // as `hotfixes`. Will update `response` with the signal collection outcome,
+ // and invoke `done_closure` to asynchronously notify the caller of the
+ // completion of this request.
+ void OnHotfixSignalCollected(SignalsAggregationResponse& response,
+ base::OnceClosure done_closure,
const std::vector<InstalledHotfix>& hotfixes);
// Instance used to retrieve a pointer to a SystemSignalsService instance.
base::raw_ptr<SystemSignalsServiceHost> system_service_host_;
- // Map used to forward signal collection requests to the right function keyed
- // from a given signal name.
- std::map<const std::string,
- base::RepeatingCallback<void(const base::Value&, GetSignalCallback)>>
- signals_collection_map_;
-
+ SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<WinSignalsCollector> weak_factory_{this};
};
diff --git a/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc b/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc
index cd95070e9f9..ea6ab72d5cb 100644
--- a/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc
+++ b/chromium/components/device_signals/core/browser/win/win_signals_collector_unittest.cc
@@ -6,10 +6,11 @@
#include <array>
+#include "base/run_loop.h"
#include "base/test/task_environment.h"
-#include "base/test/test_future.h"
#include "base/values.h"
#include "components/device_signals/core/browser/mock_system_signals_service_host.h"
+#include "components/device_signals/core/browser/signals_types.h"
#include "components/device_signals/core/common/signals_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,6 +33,12 @@ class WinSignalsCollectorTest : public testing::Test {
ON_CALL(service_host_, GetService()).WillByDefault(Return(&service_));
}
+ SignalsAggregationRequest CreateRequest(SignalName signal_name) {
+ SignalsAggregationRequest request;
+ request.signal_names.emplace(signal_name);
+ return request;
+ }
+
base::test::TaskEnvironment task_environment_;
StrictMock<MockSystemSignalsServiceHost> service_host_;
@@ -42,8 +49,8 @@ class WinSignalsCollectorTest : public testing::Test {
// Test that runs a sanity check on the set of signals supported by this
// collector. Will need to be updated if new signals become supported.
TEST_F(WinSignalsCollectorTest, SupportedSignalNames) {
- const std::array<std::string, 2> supported_signals{
- {names::kAntiVirusInfo, names::kInstalledHotfixes}};
+ const std::array<SignalName, 2> supported_signals{
+ {SignalName::kAntiVirus, SignalName::kHotfixes}};
const auto names_set = win_collector_.GetSupportedSignalNames();
@@ -55,22 +62,57 @@ TEST_F(WinSignalsCollectorTest, SupportedSignalNames) {
// Tests that an unsupported signal is marked as unsupported.
TEST_F(WinSignalsCollectorTest, GetSignal_Unsupported) {
- base::test::TestFuture<base::Value> future;
- win_collector_.GetSignal(names::kDeviceId, base::Value(),
- future.GetCallback());
- EXPECT_EQ(future.Get(), base::Value(errors::kUnsupported));
+ SignalName signal_name = SignalName::kFileSystemInfo;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ win_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_TRUE(response.top_level_error.has_value());
+ EXPECT_EQ(response.top_level_error.value(),
+ SignalCollectionError::kUnsupported);
}
// Tests that not being able to retrieve a pointer to the SystemSignalsService
// returns an error.
-TEST_F(WinSignalsCollectorTest, GetSignal_MissingSystemSignalsService) {
- for (const auto& signal_name : win_collector_.GetSupportedSignalNames()) {
- EXPECT_CALL(service_host_, GetService()).WillOnce(Return(nullptr));
+TEST_F(WinSignalsCollectorTest, GetSignal_AV_MissingSystemSignalsService) {
+ EXPECT_CALL(service_host_, GetService()).WillOnce(Return(nullptr));
+
+ SignalName signal_name = SignalName::kAntiVirus;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ win_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.av_signal_response.has_value());
+ ASSERT_TRUE(response.av_signal_response->collection_error.has_value());
+ EXPECT_EQ(response.av_signal_response->collection_error.value(),
+ SignalCollectionError::kMissingSystemService);
+}
- base::test::TestFuture<base::Value> future;
- win_collector_.GetSignal(signal_name, base::Value(), future.GetCallback());
- EXPECT_EQ(future.Get(), base::Value(errors::kMissingSystemService));
- }
+// Tests that not being able to retrieve a pointer to the SystemSignalsService
+// returns an error.
+TEST_F(WinSignalsCollectorTest, GetSignal_Hotfix_MissingSystemSignalsService) {
+ EXPECT_CALL(service_host_, GetService()).WillOnce(Return(nullptr));
+
+ SignalName signal_name = SignalName::kHotfixes;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ win_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+
+ ASSERT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.hotfix_signal_response.has_value());
+ ASSERT_TRUE(response.hotfix_signal_response->collection_error.has_value());
+ EXPECT_EQ(response.hotfix_signal_response->collection_error.value(),
+ SignalCollectionError::kMissingSystemService);
}
// Tests a successful AV signal retrieval.
@@ -86,15 +128,20 @@ TEST_F(WinSignalsCollectorTest, GetSignal_AV) {
std::move(signal_callback).Run(av_products);
}));
- base::test::TestFuture<base::Value> future;
- win_collector_.GetSignal(names::kAntiVirusInfo, base::Value(),
- future.GetCallback());
+ SignalName signal_name = SignalName::kAntiVirus;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ win_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
- const base::Value& response = future.Get();
- ASSERT_TRUE(response.is_list());
- const base::Value::List& list_value = response.GetList();
- ASSERT_EQ(list_value.size(), av_products.size());
- EXPECT_EQ(list_value[0], av_products[0].ToValue());
+ run_loop.Run();
+
+ EXPECT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.av_signal_response.has_value());
+ EXPECT_FALSE(response.av_signal_response->collection_error.has_value());
+ EXPECT_EQ(response.av_signal_response->av_products.size(),
+ av_products.size());
+ EXPECT_EQ(response.av_signal_response->av_products[0], av_products[0]);
}
// Tests a successful hotfix signal retrieval.
@@ -108,15 +155,19 @@ TEST_F(WinSignalsCollectorTest, GetSignal_Hotfix) {
std::move(signal_callback).Run(hotfixes);
}));
- base::test::TestFuture<base::Value> future;
- win_collector_.GetSignal(names::kInstalledHotfixes, base::Value(),
- future.GetCallback());
+ SignalName signal_name = SignalName::kHotfixes;
+ SignalsAggregationResponse response;
+ base::RunLoop run_loop;
+ win_collector_.GetSignal(signal_name, CreateRequest(signal_name), response,
+ run_loop.QuitClosure());
+
+ run_loop.Run();
- const base::Value& response = future.Get();
- ASSERT_TRUE(response.is_list());
- const base::Value::List& list_value = response.GetList();
- ASSERT_EQ(list_value.size(), hotfixes.size());
- EXPECT_EQ(list_value[0], hotfixes[0].ToValue());
+ EXPECT_FALSE(response.top_level_error.has_value());
+ ASSERT_TRUE(response.hotfix_signal_response.has_value());
+ EXPECT_FALSE(response.hotfix_signal_response->collection_error.has_value());
+ EXPECT_EQ(response.hotfix_signal_response->hotfixes.size(), hotfixes.size());
+ EXPECT_EQ(response.hotfix_signal_response->hotfixes[0], hotfixes[0]);
}
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/BUILD.gn b/chromium/components/device_signals/core/common/BUILD.gn
index 3d707b775e4..1a808cf3684 100644
--- a/chromium/components/device_signals/core/common/BUILD.gn
+++ b/chromium/components/device_signals/core/common/BUILD.gn
@@ -3,15 +3,42 @@
# found in the LICENSE file.
static_library("common") {
- public = [ "signals_constants.h" ]
+ public = [
+ "common_types.h",
+ "signals_constants.h",
+ ]
- sources = [ "signals_constants.cc" ]
+ sources = [
+ "common_types.cc",
+ "signals_constants.cc",
+ ]
+
+ public_deps = [ "//third_party/abseil-cpp:absl" ]
+
+ deps = [ "//base" ]
+}
+
+source_set("features") {
+ public = [ "signals_features.h" ]
+
+ sources = [ "signals_features.cc" ]
+
+ public_deps = [ "//base" ]
}
source_set("unit_tests") {
testonly = true
+ sources = [ "signals_features_unittest.cc" ]
+ deps = [
+ ":common",
+ ":features",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
if (is_win) {
- deps = [ "//components/device_signals/core/common/win:unit_tests" ]
+ deps += [ "//components/device_signals/core/common/win:unit_tests" ]
}
}
diff --git a/chromium/components/device_signals/core/common/common_types.cc b/chromium/components/device_signals/core/common/common_types.cc
new file mode 100644
index 00000000000..078724ea3a2
--- /dev/null
+++ b/chromium/components/device_signals/core/common/common_types.cc
@@ -0,0 +1,52 @@
+// 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 "components/device_signals/core/common/common_types.h"
+
+namespace device_signals {
+
+ExecutableMetadata::ExecutableMetadata() = default;
+
+ExecutableMetadata::ExecutableMetadata(const ExecutableMetadata&) = default;
+ExecutableMetadata& ExecutableMetadata::operator=(const ExecutableMetadata&) =
+ default;
+
+ExecutableMetadata::~ExecutableMetadata() = default;
+
+bool ExecutableMetadata::operator==(const ExecutableMetadata& other) const {
+ return is_running == other.is_running &&
+ public_key_sha256 == other.public_key_sha256 &&
+ product_name == other.product_name && version == other.version;
+}
+
+FileSystemItem::FileSystemItem() = default;
+
+FileSystemItem::FileSystemItem(const FileSystemItem&) = default;
+FileSystemItem& FileSystemItem::operator=(const FileSystemItem&) = default;
+
+FileSystemItem::~FileSystemItem() = default;
+
+bool FileSystemItem::operator==(const FileSystemItem& other) const {
+ return file_path == other.file_path && presence == other.presence &&
+ sha256_hash == other.sha256_hash &&
+ executable_metadata == other.executable_metadata;
+}
+
+GetFileSystemInfoOptions::GetFileSystemInfoOptions() = default;
+
+GetFileSystemInfoOptions::GetFileSystemInfoOptions(
+ const GetFileSystemInfoOptions&) = default;
+GetFileSystemInfoOptions& GetFileSystemInfoOptions::operator=(
+ const GetFileSystemInfoOptions&) = default;
+
+GetFileSystemInfoOptions::~GetFileSystemInfoOptions() = default;
+
+bool GetFileSystemInfoOptions::operator==(
+ const GetFileSystemInfoOptions& other) const {
+ return file_path == other.file_path &&
+ compute_sha256 == other.compute_sha256 &&
+ compute_executable_metadata == other.compute_executable_metadata;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/common_types.h b/chromium/components/device_signals/core/common/common_types.h
new file mode 100644
index 00000000000..f085163573f
--- /dev/null
+++ b/chromium/components/device_signals/core/common/common_types.h
@@ -0,0 +1,89 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_COMMON_TYPES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_COMMON_TYPES_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace device_signals {
+
+// Used to indicate whether a given signal was correctly found or not, or
+// indicate a reason for not being able to find it.
+enum class PresenceValue { kUnspecified, kAccessDenied, kNotFound, kFound };
+
+struct ExecutableMetadata {
+ ExecutableMetadata();
+
+ ExecutableMetadata(const ExecutableMetadata&);
+ ExecutableMetadata& operator=(const ExecutableMetadata&);
+
+ ~ExecutableMetadata();
+
+ // Is true if a currently running process was spawned from this file.
+ bool is_running = false;
+
+ // Byte string containing the SHA256 hash of the public key of the certificate
+ // used to sign the executable.
+ absl::optional<std::string> public_key_sha256 = absl::nullopt;
+
+ // Product name of this executable.
+ absl::optional<std::string> product_name = absl::nullopt;
+
+ // Version of this executable.
+ absl::optional<std::string> version = absl::nullopt;
+
+ bool operator==(const ExecutableMetadata& other) const;
+};
+
+struct FileSystemItem {
+ FileSystemItem();
+
+ FileSystemItem(const FileSystemItem&);
+ FileSystemItem& operator=(const FileSystemItem&);
+
+ ~FileSystemItem();
+
+ // Path to the file system object for whom those signals were collected.
+ base::FilePath file_path{};
+
+ // Value indicating whether the specific resource could be found or not.
+ PresenceValue presence = PresenceValue::kUnspecified;
+
+ // Byte string containing the SHA256 hash of a file’s bytes. Ignored when
+ // `path` points to a directory. Collected only when `compute_sha256` is set
+ // to true in the corresponding GetFileSystemInfoOptions parameter.
+ absl::optional<std::string> sha256_hash = absl::nullopt;
+
+ // Set of properties only relevant for executable files. Will only be
+ // collected if computeIsExecutable is set to true in the given signals
+ // collection parameters and if `path` points to an executable file.
+ absl::optional<ExecutableMetadata> executable_metadata = absl::nullopt;
+
+ bool operator==(const FileSystemItem& other) const;
+};
+
+struct GetFileSystemInfoOptions {
+ GetFileSystemInfoOptions();
+
+ GetFileSystemInfoOptions(const GetFileSystemInfoOptions&);
+ GetFileSystemInfoOptions& operator=(const GetFileSystemInfoOptions&);
+
+ ~GetFileSystemInfoOptions();
+
+ base::FilePath file_path{};
+
+ bool compute_sha256 = false;
+
+ bool compute_executable_metadata = false;
+
+ bool operator==(const GetFileSystemInfoOptions& other) const;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_COMMON_TYPES_H_
diff --git a/chromium/components/device_signals/core/common/mojom/BUILD.gn b/chromium/components/device_signals/core/common/mojom/BUILD.gn
index 2d9e2cda1f0..0c98d32fec7 100644
--- a/chromium/components/device_signals/core/common/mojom/BUILD.gn
+++ b/chromium/components/device_signals/core/common/mojom/BUILD.gn
@@ -12,8 +12,38 @@ mojom("mojom") {
"//sandbox/policy/mojom",
]
+ cpp_typemaps = [
+ {
+ types = [
+ {
+ mojom = "device_signals.mojom.PresenceValue"
+ cpp = "::device_signals::PresenceValue"
+ },
+ {
+ mojom = "device_signals.mojom.ExecutableMetadata"
+ cpp = "::device_signals::ExecutableMetadata"
+ },
+ {
+ mojom = "device_signals.mojom.FileSystemItem"
+ cpp = "::device_signals::FileSystemItem"
+ },
+ {
+ mojom = "device_signals.mojom.FileSystemItemRequest"
+ cpp = "::device_signals::GetFileSystemInfoOptions"
+ },
+ ]
+ traits_headers = [ "system_signals_mojom_traits_common.h" ]
+ traits_sources = [ "system_signals_mojom_traits_common.cc" ]
+ traits_public_deps = [
+ "//base",
+ "//components/device_signals/core/common",
+ "//mojo/public/cpp/base:shared_typemap_traits",
+ ]
+ },
+ ]
+
if (is_win) {
- cpp_typemaps = [
+ cpp_typemaps += [
{
types = [
{
@@ -29,8 +59,8 @@ mojom("mojom") {
cpp = "::device_signals::InstalledHotfix"
},
]
- traits_headers = [ "system_signals_mojom_traits.h" ]
- traits_sources = [ "system_signals_mojom_traits.cc" ]
+ traits_headers = [ "system_signals_mojom_traits_win.h" ]
+ traits_sources = [ "system_signals_mojom_traits_win.cc" ]
traits_public_deps = [
"//base",
"//components/device_signals/core/common/win",
diff --git a/chromium/components/device_signals/core/common/mojom/DEPS b/chromium/components/device_signals/core/common/mojom/DEPS
new file mode 100644
index 00000000000..093b1d9fde5
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/cpp",
+]
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals.mojom b/chromium/components/device_signals/core/common/mojom/system_signals.mojom
index f335e665684..0967e937b6e 100644
--- a/chromium/components/device_signals/core/common/mojom/system_signals.mojom
+++ b/chromium/components/device_signals/core/common/mojom/system_signals.mojom
@@ -4,32 +4,45 @@
module device_signals.mojom;
-import "mojo/public/mojom/base/file_path.mojom";
import "sandbox/policy/mojom/sandbox.mojom";
+import "mojo/public/mojom/base/byte_string.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
-enum TrileanValue {
- kFalse,
- kTrue,
- kUnknown
-};
+enum PresenceValue {
+ // Was unable to determine whether the signal source exists or not. This
+ // typically indicates that a failure occurred before even trying to assess
+ // its presence.
+ kUnspecified,
-struct BinarySignalsRequest {
- mojo_base.mojom.FilePath file_path;
-};
+ // Unable to determine the signal source's existence due to insufficient user
+ // permissions.
+ kAccessDenied,
-struct BinarySignalsResponse {
- mojo_base.mojom.FilePath file_path;
+ // The signal's source does not exist.
+ kNotFound,
- bool is_running;
+ // The signal's source was found.
+ kFound
+};
- [EnableIfNot=is_linux]
- string product_name;
+struct ExecutableMetadata {
+ bool is_running;
+ mojo_base.mojom.ByteString? public_key_sha256;
+ string? product_name;
+ string? version;
+};
- [EnableIfNot=is_linux]
- string public_key_sha256;
+struct FileSystemItem {
+ mojo_base.mojom.FilePath file_path;
+ PresenceValue presence;
+ mojo_base.mojom.ByteString? sha256_hash;
+ ExecutableMetadata? executable_metadata;
+};
- [EnableIfNot=is_linux]
- string version;
+struct FileSystemItemRequest {
+ mojo_base.mojom.FilePath file_path;
+ bool compute_sha256;
+ bool compute_executable_metadata;
};
[EnableIf=is_win]
@@ -64,10 +77,10 @@ struct HotfixSignal {
// This service can be accessed from the browser process.
[ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox]
interface SystemSignalsService {
- // Collects signals about a set of binary files specified by `requests`.
- // Returns the collected information in `response`.
- GetBinarySignals(array<BinarySignalsRequest> requests)
- => (array<BinarySignalsResponse> response);
+ // Collects signals about a set of file system items specified by `requests`.
+ // Returns the collected information in `items`.
+ GetFileSystemSignals(array<FileSystemItemRequest> requests)
+ => (array<FileSystemItem> items);
// Collects information about AntiVirus software installed on the current
// device. Returns that information via `av_signals`.
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.cc b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.cc
new file mode 100644
index 00000000000..64780ef9926
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.cc
@@ -0,0 +1,104 @@
+// 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 "components/device_signals/core/common/mojom/system_signals_mojom_traits_common.h"
+
+#include "base/notreached.h"
+#include "mojo/public/cpp/base/byte_string_mojom_traits.h"
+#include "mojo/public/cpp/base/file_path_mojom_traits.h"
+
+namespace mojo {
+
+// static
+device_signals::mojom::PresenceValue
+EnumTraits<device_signals::mojom::PresenceValue,
+ device_signals::PresenceValue>::ToMojom(device_signals::PresenceValue
+ input) {
+ switch (input) {
+ case device_signals::PresenceValue::kUnspecified:
+ return device_signals::mojom::PresenceValue::kUnspecified;
+ case device_signals::PresenceValue::kAccessDenied:
+ return device_signals::mojom::PresenceValue::kAccessDenied;
+ case device_signals::PresenceValue::kNotFound:
+ return device_signals::mojom::PresenceValue::kNotFound;
+ case device_signals::PresenceValue::kFound:
+ return device_signals::mojom::PresenceValue::kFound;
+ }
+}
+
+// static
+bool EnumTraits<device_signals::mojom::PresenceValue,
+ device_signals::PresenceValue>::
+ FromMojom(device_signals::mojom::PresenceValue input,
+ device_signals::PresenceValue* output) {
+ absl::optional<device_signals::PresenceValue> parsed_value;
+ switch (input) {
+ case device_signals::mojom::PresenceValue::kUnspecified:
+ parsed_value = device_signals::PresenceValue::kUnspecified;
+ break;
+ case device_signals::mojom::PresenceValue::kAccessDenied:
+ parsed_value = device_signals::PresenceValue::kAccessDenied;
+ break;
+ case device_signals::mojom::PresenceValue::kNotFound:
+ parsed_value = device_signals::PresenceValue::kNotFound;
+ break;
+ case device_signals::mojom::PresenceValue::kFound:
+ parsed_value = device_signals::PresenceValue::kFound;
+ break;
+ }
+
+ if (parsed_value.has_value()) {
+ *output = parsed_value.value();
+ return true;
+ }
+ return false;
+}
+
+// static
+bool StructTraits<device_signals::mojom::ExecutableMetadataDataView,
+ device_signals::ExecutableMetadata>::
+ Read(device_signals::mojom::ExecutableMetadataDataView data,
+ device_signals::ExecutableMetadata* output) {
+ output->is_running = data.is_running();
+
+ if (!data.ReadPublicKeySha256(&output->public_key_sha256) ||
+ !data.ReadProductName(&output->product_name) ||
+ !data.ReadVersion(&output->version)) {
+ return false;
+ }
+
+ return true;
+}
+
+// static
+bool StructTraits<device_signals::mojom::FileSystemItemDataView,
+ device_signals::FileSystemItem>::
+ Read(device_signals::mojom::FileSystemItemDataView data,
+ device_signals::FileSystemItem* output) {
+ if (!data.ReadFilePath(&output->file_path) ||
+ !data.ReadPresence(&output->presence) ||
+ !data.ReadSha256Hash(&output->sha256_hash) ||
+ !data.ReadExecutableMetadata(&output->executable_metadata)) {
+ return false;
+ }
+
+ return true;
+}
+
+// static
+bool StructTraits<device_signals::mojom::FileSystemItemRequestDataView,
+ device_signals::GetFileSystemInfoOptions>::
+ Read(device_signals::mojom::FileSystemItemRequestDataView data,
+ device_signals::GetFileSystemInfoOptions* output) {
+ output->compute_sha256 = data.compute_sha256();
+ output->compute_executable_metadata = data.compute_executable_metadata();
+
+ if (!data.ReadFilePath(&output->file_path)) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.h b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.h
new file mode 100644
index 00000000000..fedcc7b84ed
--- /dev/null
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_common.h
@@ -0,0 +1,103 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_COMMON_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_COMMON_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/common/mojom/system_signals.mojom-shared.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<device_signals::mojom::PresenceValue,
+ device_signals::PresenceValue> {
+ static device_signals::mojom::PresenceValue ToMojom(
+ device_signals::PresenceValue input);
+ static bool FromMojom(device_signals::mojom::PresenceValue input,
+ device_signals::PresenceValue* output);
+};
+
+template <>
+struct StructTraits<device_signals::mojom::ExecutableMetadataDataView,
+ device_signals::ExecutableMetadata> {
+ static bool is_running(const device_signals::ExecutableMetadata& input) {
+ return input.is_running;
+ }
+
+ static absl::optional<std::string> public_key_sha256(
+ const device_signals::ExecutableMetadata& input) {
+ return input.public_key_sha256;
+ }
+
+ static absl::optional<std::string> product_name(
+ const device_signals::ExecutableMetadata& input) {
+ return input.product_name;
+ }
+
+ static absl::optional<std::string> version(
+ const device_signals::ExecutableMetadata& input) {
+ return input.version;
+ }
+
+ static bool Read(device_signals::mojom::ExecutableMetadataDataView data,
+ device_signals::ExecutableMetadata* output);
+};
+
+template <>
+struct StructTraits<device_signals::mojom::FileSystemItemDataView,
+ device_signals::FileSystemItem> {
+ static const base::FilePath& file_path(
+ const device_signals::FileSystemItem& input) {
+ return input.file_path;
+ }
+
+ static device_signals::PresenceValue presence(
+ const device_signals::FileSystemItem& input) {
+ return input.presence;
+ }
+
+ static absl::optional<std::string> sha256_hash(
+ const device_signals::FileSystemItem& input) {
+ return input.sha256_hash;
+ }
+
+ static absl::optional<device_signals::ExecutableMetadata> executable_metadata(
+ const device_signals::FileSystemItem& input) {
+ return input.executable_metadata;
+ }
+
+ static bool Read(device_signals::mojom::FileSystemItemDataView input,
+ device_signals::FileSystemItem* output);
+};
+
+template <>
+struct StructTraits<device_signals::mojom::FileSystemItemRequestDataView,
+ device_signals::GetFileSystemInfoOptions> {
+ static const base::FilePath& file_path(
+ const device_signals::GetFileSystemInfoOptions& input) {
+ return input.file_path;
+ }
+
+ static bool compute_sha256(
+ const device_signals::GetFileSystemInfoOptions& input) {
+ return input.compute_sha256;
+ }
+
+ static bool compute_executable_metadata(
+ const device_signals::GetFileSystemInfoOptions& input) {
+ return input.compute_executable_metadata;
+ }
+
+ static bool Read(device_signals::mojom::FileSystemItemRequestDataView input,
+ device_signals::GetFileSystemInfoOptions* output);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_COMMON_H_
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.cc
index 22daec5f4ce..6d14d5b91ee 100644
--- a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/device_signals/core/common/mojom/system_signals_mojom_traits.h"
+#include "components/device_signals/core/common/mojom/system_signals_mojom_traits_win.h"
#include "base/notreached.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.h
index 7b1f5469a03..0600be46eb8 100644
--- a/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits.h
+++ b/chromium/components/device_signals/core/common/mojom/system_signals_mojom_traits_win.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_WIN_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_WIN_H_
#include <string>
@@ -56,4 +56,4 @@ struct StructTraits<device_signals::mojom::HotfixSignalDataView,
} // namespace mojo
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_H_ \ No newline at end of file
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_MOJOM_SYSTEM_SIGNALS_MOJOM_TRAITS_WIN_H_
diff --git a/chromium/components/device_signals/core/common/signals_constants.cc b/chromium/components/device_signals/core/common/signals_constants.cc
index 021c7b759fb..bf2c5c72969 100644
--- a/chromium/components/device_signals/core/common/signals_constants.cc
+++ b/chromium/components/device_signals/core/common/signals_constants.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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.
@@ -144,6 +144,10 @@ namespace errors {
// to be collected.
const char kConsentRequired[] = "CONSENT_REQUIRED";
+// Returned when the user does not represent the current browser user, or is
+// not managed.
+const char kInvalidUser[] = "INVALID_USER";
+
// Returned when the user is not affiliated with the organization managing the
// browser.
const char kUnaffiliatedUser[] = "UNAFFILIATED_USER";
@@ -155,6 +159,14 @@ const char kUnsupported[] = "UNSUPPORTED";
// the SystemSignalsService.
const char kMissingSystemService[] = "MISSING_SYSTEM_SERVICE";
+// Returned when the signals aggregation response is missing a
+// bundle/sub-response struct that was expected by a specific use-case.
+const char kMissingBundle[] = "MISSING_BUNDLE";
+
+// Returned when requesting the collection of a parameterized signal without
+// parameters.
+const char kMissingParameters[] = "MISSING_PARAMETERS";
+
} // namespace errors
} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/signals_constants.h b/chromium/components/device_signals/core/common/signals_constants.h
index b78cdba4562..ad4ade35852 100644
--- a/chromium/components/device_signals/core/common/signals_constants.h
+++ b/chromium/components/device_signals/core/common/signals_constants.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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.
@@ -52,8 +52,11 @@ namespace errors {
extern const char kConsentRequired[];
extern const char kUnaffiliatedUser[];
+extern const char kInvalidUser[];
extern const char kUnsupported[];
extern const char kMissingSystemService[];
+extern const char kMissingBundle[];
+extern const char kMissingParameters[];
} // namespace errors
diff --git a/chromium/components/device_signals/core/common/signals_features.cc b/chromium/components/device_signals/core/common/signals_features.cc
new file mode 100644
index 00000000000..cfb98c6223d
--- /dev/null
+++ b/chromium/components/device_signals/core/common/signals_features.cc
@@ -0,0 +1,55 @@
+// 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 "components/device_signals/core/common/signals_features.h"
+
+namespace enterprise_signals::features {
+
+const base::Feature kNewEvSignalsEnabled = {"NewEvSignalsEnabled",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::FeatureParam<bool> kDisableFileSystemInfo{
+ &kNewEvSignalsEnabled, "DisableFileSystemInfo", false};
+const base::FeatureParam<bool> kDisableSettings{&kNewEvSignalsEnabled,
+ "DisableSettings", false};
+const base::FeatureParam<bool> kDisableAntiVirus{&kNewEvSignalsEnabled,
+ "DisableAntiVirus", false};
+const base::FeatureParam<bool> kDisableHotfix{&kNewEvSignalsEnabled,
+ "DisableHotfix", false};
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+// Enables the consent promo for sharing device signal when a managed user
+// signs in on an unmanaged device. This occurs after the sign-in intercept
+// and before the sync promo (if enabled)
+// This feature also requires UnmanagedDeviceSignalsConsentFlowEnabled policy to
+// be enabled
+const base::Feature kDeviceSignalsPromoAfterSigninIntercept{
+ "DeviceSignalsPromoAfterSigninIntercept",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+
+bool IsNewFunctionEnabled(NewEvFunction new_ev_function) {
+ if (!base::FeatureList::IsEnabled(kNewEvSignalsEnabled)) {
+ return false;
+ }
+
+ bool disable_function = false;
+ switch (new_ev_function) {
+ case NewEvFunction::kFileSystemInfo:
+ disable_function = kDisableFileSystemInfo.Get();
+ break;
+ case NewEvFunction::kSettings:
+ disable_function = kDisableSettings.Get();
+ break;
+ case NewEvFunction::kAntiVirus:
+ disable_function = kDisableAntiVirus.Get();
+ break;
+ case NewEvFunction::kHotfix:
+ disable_function = kDisableHotfix.Get();
+ break;
+ }
+ return !disable_function;
+}
+
+} // namespace enterprise_signals::features
diff --git a/chromium/components/device_signals/core/common/signals_features.h b/chromium/components/device_signals/core/common/signals_features.h
new file mode 100644
index 00000000000..a3dc50abb0f
--- /dev/null
+++ b/chromium/components/device_signals/core/common/signals_features.h
@@ -0,0 +1,36 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_FEATURES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+
+namespace enterprise_signals::features {
+
+// Feature flag for new private SecureConnect functions exposing additional
+// device signals.
+extern const base::Feature kNewEvSignalsEnabled;
+
+// Feature parameters that can be used to turn off individual functions.
+extern const base::FeatureParam<bool> kDisableFileSystemInfo;
+extern const base::FeatureParam<bool> kDisableSettings;
+extern const base::FeatureParam<bool> kDisableAntiVirus;
+extern const base::FeatureParam<bool> kDisableHotfix;
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+extern const base::Feature kDeviceSignalsPromoAfterSigninIntercept;
+#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+
+// Enum used to map a given function to its kill switch.
+enum class NewEvFunction { kFileSystemInfo, kSettings, kAntiVirus, kHotfix };
+
+// Returns true if the function pointed at by `new_ev_function` is considered
+// to be enabled based on the feature flag and its parameters.
+bool IsNewFunctionEnabled(NewEvFunction new_ev_function);
+
+} // namespace enterprise_signals::features
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_SIGNALS_FEATURES_H_
diff --git a/chromium/components/device_signals/core/common/signals_features_unittest.cc b/chromium/components/device_signals/core/common/signals_features_unittest.cc
new file mode 100644
index 00000000000..7ecf6d6c80d
--- /dev/null
+++ b/chromium/components/device_signals/core/common/signals_features_unittest.cc
@@ -0,0 +1,89 @@
+// 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 "components/device_signals/core/common/signals_features.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace enterprise_signals::features {
+
+class SignalsFeaturesTest : public testing::Test {
+ protected:
+ const int min_enum_value_ = static_cast<int>(NewEvFunction::kFileSystemInfo);
+ const int max_enum_value_ = static_cast<int>(NewEvFunction::kHotfix);
+ base::test::ScopedFeatureList scoped_features_;
+};
+
+// Tests that IsNewFunctionEnabled will return false when the feature flag is
+// disabled.
+TEST_F(SignalsFeaturesTest, DisabledFeature) {
+ scoped_features_.InitAndDisableFeature(kNewEvSignalsEnabled);
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ EXPECT_FALSE(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)));
+ }
+}
+
+// Tests that IsNewFunctionEnabled will return true when the feature flag is
+// enabled, and no specific function is disabled.
+TEST_F(SignalsFeaturesTest, EnabledFeature) {
+ scoped_features_.InitAndEnableFeature(kNewEvSignalsEnabled);
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ EXPECT_TRUE(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)));
+ }
+}
+
+// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
+// the FileSystemInfo function is disabled.
+TEST_F(SignalsFeaturesTest, Enabled_DisableFileSystemInfo) {
+ scoped_features_.InitAndEnableFeatureWithParameters(
+ kNewEvSignalsEnabled, {{"DisableFileSystemInfo", "true"}});
+
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ NewEvFunction current_function = static_cast<NewEvFunction>(i);
+ EXPECT_EQ(IsNewFunctionEnabled(current_function),
+ current_function != NewEvFunction::kFileSystemInfo);
+ }
+}
+
+// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
+// the Settings function is disabled.
+TEST_F(SignalsFeaturesTest, Enabled_DisableSetting) {
+ scoped_features_.InitAndEnableFeatureWithParameters(
+ kNewEvSignalsEnabled, {{"DisableSettings", "true"}});
+
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ NewEvFunction current_function = static_cast<NewEvFunction>(i);
+ EXPECT_EQ(IsNewFunctionEnabled(current_function),
+ current_function != NewEvFunction::kSettings);
+ }
+}
+
+// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
+// the Antivirus function is disabled.
+TEST_F(SignalsFeaturesTest, Enabled_DisableAntiVirus) {
+ scoped_features_.InitAndEnableFeatureWithParameters(
+ kNewEvSignalsEnabled, {{"DisableAntiVirus", "true"}});
+
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ NewEvFunction current_function = static_cast<NewEvFunction>(i);
+ EXPECT_EQ(IsNewFunctionEnabled(current_function),
+ current_function != NewEvFunction::kAntiVirus);
+ }
+}
+
+// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
+// the Hotfix function is disabled.
+TEST_F(SignalsFeaturesTest, Enabled_DisableHotfix) {
+ scoped_features_.InitAndEnableFeatureWithParameters(
+ kNewEvSignalsEnabled, {{"DisableHotfix", "true"}});
+
+ for (int i = min_enum_value_; i <= max_enum_value_; i++) {
+ NewEvFunction current_function = static_cast<NewEvFunction>(i);
+ EXPECT_EQ(IsNewFunctionEnabled(current_function),
+ current_function != NewEvFunction::kHotfix);
+ }
+}
+
+} // namespace enterprise_signals::features
diff --git a/chromium/components/device_signals/core/common/win/BUILD.gn b/chromium/components/device_signals/core/common/win/BUILD.gn
index 048ed98ffa6..dfdcd91886c 100644
--- a/chromium/components/device_signals/core/common/win/BUILD.gn
+++ b/chromium/components/device_signals/core/common/win/BUILD.gn
@@ -3,63 +3,20 @@
# found in the LICENSE file.
static_library("win") {
- public = [
- "win_types.h",
- "wmi_client.h",
- "wmi_client_impl.h",
- "wsc_client.h",
- "wsc_client_impl.h",
- ]
+ public = [ "win_types.h" ]
- sources = [
- "win_types.cc",
- "wmi_client.cc",
- "wmi_client_impl.cc",
- "wsc_client.cc",
- "wsc_client_impl.cc",
- ]
+ sources = [ "win_types.cc" ]
- public_deps = [
- "//base",
- "//third_party/abseil-cpp:absl",
- ]
-}
-
-source_set("test_support") {
- testonly = true
- sources = [
- "com_fakes.cc",
- "com_fakes.h",
- "mock_wmi_client.cc",
- "mock_wmi_client.h",
- "mock_wsc_client.cc",
- "mock_wsc_client.h",
- ]
-
- deps = [
- ":win",
- "//base",
- "//testing/gmock",
- "//testing/gtest",
- "//third_party/abseil-cpp:absl",
- ]
+ deps = [ "//base" ]
}
source_set("unit_tests") {
testonly = true
- sources = [
- "win_types_unittest.cc",
- "wmi_client_impl_unittest.cc",
- "wsc_client_impl_unittest.cc",
- ]
+ sources = [ "win_types_unittest.cc" ]
deps = [
- ":test_support",
":win",
"//base",
- "//base/test:test_support",
- "//testing/gmock",
"//testing/gtest",
- "//third_party/abseil-cpp:absl",
]
}
diff --git a/chromium/components/device_signals/core/common/win/mock_wmi_client.h b/chromium/components/device_signals/core/common/win/mock_wmi_client.h
deleted file mode 100644
index ac934b7b9a4..00000000000
--- a/chromium/components/device_signals/core/common/win/mock_wmi_client.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
-
-#include "components/device_signals/core/common/win/wmi_client.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace device_signals {
-
-class MockWmiClient : public WmiClient {
- public:
- MockWmiClient();
- ~MockWmiClient() override;
-
- MOCK_METHOD(WmiHotfixesResponse, GetInstalledHotfixes, (), (override));
-};
-
-} // namespace device_signals
-
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/win_types.cc b/chromium/components/device_signals/core/common/win/win_types.cc
index 8add6652e48..18b1c574282 100644
--- a/chromium/components/device_signals/core/common/win/win_types.cc
+++ b/chromium/components/device_signals/core/common/win/win_types.cc
@@ -8,6 +8,11 @@
namespace device_signals {
+bool AvProduct::operator==(const AvProduct& other) const {
+ return display_name == other.display_name && state == other.state &&
+ product_id == other.product_id;
+}
+
base::Value AvProduct::ToValue() const {
base::Value::Dict values;
values.Set("displayName", display_name);
@@ -16,6 +21,10 @@ base::Value AvProduct::ToValue() const {
return base::Value(std::move(values));
}
+bool InstalledHotfix::operator==(const InstalledHotfix& other) const {
+ return hotfix_id == other.hotfix_id;
+}
+
base::Value InstalledHotfix::ToValue() const {
base::Value::Dict values;
values.Set("hotfixId", hotfix_id);
diff --git a/chromium/components/device_signals/core/common/win/win_types.h b/chromium/components/device_signals/core/common/win/win_types.h
index d551d48d2b5..a90b01ca080 100644
--- a/chromium/components/device_signals/core/common/win/win_types.h
+++ b/chromium/components/device_signals/core/common/win/win_types.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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.
@@ -20,14 +20,18 @@ enum class AvProductState { kOn, kOff, kSnoozed, kExpired };
// Can be retrieve via WSC on Windows 8 and above, and below properties are
// collected via this interface:
// https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/nn-iwscapi-iwscproduct
+// On Win7 and below, this can be retrieve by an undocumented method in WMI,
+// which goes through the SecurityCenter2 WMI server.
struct AvProduct {
- std::string display_name;
- AvProductState state;
+ std::string display_name{};
+ AvProductState state = AvProductState::kOff;
// Although not present on the documentation, IWscProduct exposes a
// `get_ProductGuid` function to retrieve an GUID representing an Antivirus
// software.
- std::string product_id;
+ std::string product_id{};
+
+ bool operator==(const AvProduct& other) const;
base::Value ToValue() const;
};
@@ -37,7 +41,9 @@ struct InstalledHotfix {
// In WMI, this value represents the `HotFixID` property from entries in
// "Win32_QuickFixEngineering". They have a format looking like `KB123123`.
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-quickfixengineering
- std::string hotfix_id;
+ std::string hotfix_id{};
+
+ bool operator==(const InstalledHotfix& other) const;
base::Value ToValue() const;
};
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl.cc b/chromium/components/device_signals/core/common/win/wmi_client_impl.cc
deleted file mode 100644
index 40b68264e2e..00000000000
--- a/chromium/components/device_signals/core/common/win/wmi_client_impl.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 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 "components/device_signals/core/common/win/wmi_client_impl.h"
-
-#include <wbemidl.h>
-#include <windows.h>
-#include <wrl/client.h>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/check.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "base/win/com_init_util.h"
-#include "base/win/scoped_bstr.h"
-#include "base/win/scoped_variant.h"
-#include "base/win/windows_version.h"
-#include "base/win/wmi.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-using Microsoft::WRL::ComPtr;
-
-namespace device_signals {
-
-namespace {
-
-// Parses a string value from `class_object` named `property_name`.
-absl::optional<std::string> ParseString(
- const std::wstring& property_name,
- const ComPtr<IWbemClassObject>& class_object) {
- base::win::ScopedVariant string_variant;
- HRESULT hr = class_object->Get(property_name.c_str(), 0,
- string_variant.Receive(), 0, 0);
-
- if (FAILED(hr) || string_variant.type() != VT_BSTR) {
- return absl::nullopt;
- }
-
- // Owned by ScopedVariant.
- BSTR temp_bstr = V_BSTR(string_variant.ptr());
- return base::SysWideToUTF8(
- std::wstring(temp_bstr, ::SysStringLen(temp_bstr)));
-}
-
-} // namespace
-
-WmiClientImpl::WmiClientImpl()
- : run_query_callback_(base::BindRepeating(base::win::RunWmiQuery)) {}
-
-WmiClientImpl::WmiClientImpl(RunWmiQueryCallback run_query_callback)
- : run_query_callback_(std::move(run_query_callback)) {}
-
-WmiClientImpl::~WmiClientImpl() = default;
-
-WmiHotfixesResponse WmiClientImpl::GetInstalledHotfixes() {
- base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
- base::BlockingType::MAY_BLOCK);
- ComPtr<IEnumWbemClassObject> enumerator;
- auto result_code = run_query_callback_.Run(
- base::win::kCimV2ServerName, L"SELECT * FROM Win32_QuickFixEngineering",
- &enumerator);
-
- WmiHotfixesResponse response;
- if (result_code.has_value()) {
- response.query_error = result_code.value();
- return response;
- }
-
- HRESULT hr;
- while (true) {
- ComPtr<IWbemClassObject> class_object;
- ULONG items_returned = 0U;
- hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
-
- if (hr == WBEM_S_FALSE || items_returned == 0U) {
- // Reached the end of the enumerator.
- break;
- }
-
- // Something went wrong, and it wasn't the end of the enumerator.
- if (FAILED(hr)) {
- response.parsing_errors.push_back(
- WmiParsingError::kFailedToIterateResults);
- continue;
- }
-
- absl::optional<std::string> hotfix_id =
- ParseString(L"HotFixId", class_object);
- if (!hotfix_id.has_value()) {
- response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
- continue;
- }
-
- InstalledHotfix hotfix;
- hotfix.hotfix_id = hotfix_id.value();
-
- // If all values were parsed properly, add `hotfix` into the response
- // vector. If any value could not be parsed properly, the item was
- // discarded.
- response.hotfixes.push_back(hotfix);
- }
-
- return response;
-}
-
-} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl.h b/chromium/components/device_signals/core/common/win/wmi_client_impl.h
deleted file mode 100644
index fa931472c97..00000000000
--- a/chromium/components/device_signals/core/common/win/wmi_client_impl.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
-
-#include "base/callback.h"
-#include "components/device_signals/core/common/win/wmi_client.h"
-
-// WMI interfaces are available on Windows Vista and above, and are officially
-// undocumented.
-namespace device_signals {
-
-class WmiClientImpl : public WmiClient {
- public:
- using RunWmiQueryCallback =
- base::RepeatingCallback<absl::optional<base::win::WmiError>(
- const std::wstring&,
- const std::wstring&,
- Microsoft::WRL::ComPtr<IEnumWbemClassObject>*)>;
-
- WmiClientImpl();
-
- ~WmiClientImpl() override;
-
- // WmiClient:
- WmiHotfixesResponse GetInstalledHotfixes() override;
-
- private:
- friend class WmiClientImplTest;
-
- // Constructor taking in a `run_query_callback` which can be used to mock
- // running the WMI query.
- explicit WmiClientImpl(RunWmiQueryCallback run_query_callback);
-
- RunWmiQueryCallback run_query_callback_;
-};
-
-} // namespace device_signals
-
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_IMPL_H_
diff --git a/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc b/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc
deleted file mode 100644
index cd83f1f4f7b..00000000000
--- a/chromium/components/device_signals/core/common/win/wmi_client_impl_unittest.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 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 "components/device_signals/core/common/win/wmi_client_impl.h"
-
-#include <wbemidl.h>
-#include <windows.h>
-#include <wrl/client.h>
-#include <wrl/implements.h>
-#include <memory>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/test/task_environment.h"
-#include "base/win/wmi.h"
-#include "components/device_signals/core/common/win/com_fakes.h"
-#include "components/device_signals/core/common/win/win_types.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-using Microsoft::WRL::ComPtr;
-
-namespace device_signals {
-
-namespace {
-
-FakeWbemClassObject CreateHotfixObject(const std::wstring& hotfix_id) {
- FakeWbemClassObject hotfix_obj;
- hotfix_obj.Set(L"HotFixId", hotfix_id);
- return hotfix_obj;
-}
-
-} // namespace
-
-class WmiClientImplTest : public testing::Test {
- public:
- WmiClientImplTest()
- : wmi_client_(base::BindRepeating(&WmiClientImplTest::RunQuery,
- base::Unretained(this))) {}
-
- protected:
- absl::optional<base::win::WmiError> RunQuery(
- const std::wstring& server_name,
- const std::wstring& query,
- ComPtr<IEnumWbemClassObject>* enumerator) {
- ++nb_calls_;
- captured_server_name_ = server_name;
- captured_query_ = query;
-
- if (query_error_.has_value()) {
- return query_error_.value();
- }
- *enumerator = &fake_enumerator_;
- return absl::nullopt;
- }
-
- void ExpectHotfixQueryRan() {
- EXPECT_EQ(captured_server_name_, base::win::kCimV2ServerName);
- EXPECT_EQ(captured_query_, L"SELECT * FROM Win32_QuickFixEngineering");
- EXPECT_EQ(nb_calls_, 1U);
- }
-
- FakeEnumWbemClassObject fake_enumerator_;
- absl::optional<base::win::WmiError> query_error_;
-
- std::wstring captured_server_name_;
- std::wstring captured_query_;
- uint32_t nb_calls_ = 0;
-
- WmiClientImpl wmi_client_;
-};
-
-// Tests how the client behaves when the WMI query fails when querying for
-// installed hotfixes.
-TEST_F(WmiClientImplTest, GetInstalledHotfixes_FailedRunQuery) {
- query_error_ = base::win::WmiError::kFailedToConnectToWMI;
-
- auto hotfix_response = wmi_client_.GetInstalledHotfixes();
-
- ExpectHotfixQueryRan();
- EXPECT_EQ(hotfix_response.hotfixes.size(), 0U);
- EXPECT_EQ(hotfix_response.parsing_errors.size(), 0U);
- EXPECT_EQ(hotfix_response.query_error,
- base::win::WmiError::kFailedToConnectToWMI);
-}
-
-// Tests how the client behaves when parsing hotfix objects, one of which is
-// valid, and another which is missing an ID.
-TEST_F(WmiClientImplTest, GetInstalledHotfixes_ParsingItems) {
- std::wstring hotfix_id1 = L"some_hotfix_id";
- FakeWbemClassObject hotfix_obj1 = CreateHotfixObject(hotfix_id1);
-
- FakeWbemClassObject hotfix_obj2 = CreateHotfixObject(L"some_other_id");
-
- hotfix_obj2.Delete(L"HotFixId");
-
- fake_enumerator_.Add(&hotfix_obj1);
- fake_enumerator_.Add(&hotfix_obj2);
-
- auto hotfix_response = wmi_client_.GetInstalledHotfixes();
-
- ExpectHotfixQueryRan();
- EXPECT_EQ(hotfix_response.query_error, absl::nullopt);
-
- // Success item.
- ASSERT_EQ(hotfix_response.hotfixes.size(), 1U);
- EXPECT_EQ(hotfix_response.hotfixes[0].hotfix_id,
- base::SysWideToUTF8(hotfix_id1));
-
- // Failed item.
- ASSERT_EQ(hotfix_response.parsing_errors.size(), 1U);
- EXPECT_EQ(hotfix_response.parsing_errors[0],
- WmiParsingError::kFailedToGetName);
-}
-
-} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/BUILD.gn b/chromium/components/device_signals/core/system_signals/BUILD.gn
new file mode 100644
index 00000000000..59de635bdd1
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/BUILD.gn
@@ -0,0 +1,80 @@
+# 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.
+
+static_library("system_signals") {
+ public = [
+ "file_system_service.h",
+ "platform_delegate.h",
+ "platform_utils.h",
+ ]
+
+ sources = [
+ "file_system_service.cc",
+ "hashing_utils.cc",
+ "hashing_utils.h",
+ "platform_delegate.cc",
+ ]
+
+ if (is_win || is_mac || is_linux) {
+ public += [ "base_platform_delegate.h" ]
+
+ sources += [ "base_platform_delegate.cc" ]
+ }
+
+ if (is_win) {
+ sources += [ "win/platform_utils_win.cc" ]
+ }
+
+ if (is_mac || is_linux) {
+ sources += [ "posix/platform_utils_posix.cc" ]
+ }
+
+ public_deps = [ "//third_party/abseil-cpp:absl" ]
+
+ deps = [
+ "//base",
+ "//components/device_signals/core/common",
+ "//crypto",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "mock_file_system_service.cc",
+ "mock_file_system_service.h",
+ "mock_platform_delegate.cc",
+ "mock_platform_delegate.h",
+ ]
+
+ deps = [
+ ":system_signals",
+ "//base",
+ "//components/device_signals/core/common",
+ "//testing/gmock",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "file_system_service_unittest.cc" ]
+
+ if (is_win || is_mac || is_linux) {
+ sources += [ "base_platform_delegate_unittest.cc" ]
+ }
+
+ deps = [
+ ":system_signals",
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//components/device_signals/core/common",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+
+ if (is_win) {
+ deps += [ "//components/device_signals/core/system_signals/win:unit_tests" ]
+ }
+}
diff --git a/chromium/components/device_signals/core/system_signals/base_platform_delegate.cc b/chromium/components/device_signals/core/system_signals/base_platform_delegate.cc
new file mode 100644
index 00000000000..5f2340ec92e
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/base_platform_delegate.cc
@@ -0,0 +1,80 @@
+// 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 "components/device_signals/core/system_signals/base_platform_delegate.h"
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/process/process_iterator.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/system_signals/platform_utils.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace device_signals {
+
+BasePlatformDelegate::BasePlatformDelegate() = default;
+BasePlatformDelegate::~BasePlatformDelegate() = default;
+
+bool BasePlatformDelegate::PathIsReadable(
+ const base::FilePath& file_path) const {
+ return base::PathIsReadable(file_path);
+}
+
+bool BasePlatformDelegate::DirectoryExists(
+ const base::FilePath& file_path) const {
+ return base::DirectoryExists(file_path);
+}
+
+FilePathMap<bool> BasePlatformDelegate::AreExecutablesRunning(
+ const FilePathSet& file_paths) {
+ // Initialize map with the given file paths.
+ FilePathMap<bool> running_map;
+ running_map.reserve(file_paths.size());
+ for (const auto& file_path : file_paths) {
+ // Default initialize as not running.
+ running_map[file_path] = false;
+ }
+
+ // Use counter to keep track of how many entries were found, which can allow
+ // for an earlier return if all executables were to be found early.
+ size_t counter = 0;
+ base::ProcessIterator process_iterator(nullptr);
+ while (const auto* process_entry = process_iterator.NextProcessEntry()) {
+ if (counter >= running_map.size()) {
+ // Found all items we were looking for, so return early.
+ break;
+ }
+
+ absl::optional<base::FilePath> exe_path =
+ GetProcessExePath(process_entry->pid());
+ if (exe_path && running_map.contains(exe_path.value())) {
+ ++counter;
+ running_map[exe_path.value()] = true;
+ }
+ }
+
+ return running_map;
+}
+
+FilePathMap<ExecutableMetadata> BasePlatformDelegate::GetAllExecutableMetadata(
+ const FilePathSet& file_paths) {
+ FilePathMap<bool> files_are_running_map = AreExecutablesRunning(file_paths);
+
+ FilePathMap<ExecutableMetadata> file_paths_to_metadata_map;
+ for (const auto& file_path : file_paths) {
+ ExecutableMetadata executable_metadata;
+
+ if (files_are_running_map.contains(file_path)) {
+ executable_metadata.is_running = files_are_running_map[file_path];
+ }
+
+ file_paths_to_metadata_map[file_path] = executable_metadata;
+ }
+
+ return file_paths_to_metadata_map;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/base_platform_delegate.h b/chromium/components/device_signals/core/system_signals/base_platform_delegate.h
new file mode 100644
index 00000000000..cbc08cc82c3
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/base_platform_delegate.h
@@ -0,0 +1,35 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_BASE_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_BASE_PLATFORM_DELEGATE_H_
+
+#include "components/device_signals/core/system_signals/platform_delegate.h"
+
+namespace device_signals {
+
+// Implements some functionality that is common to all PlatformDelegate
+// specializations.
+class BasePlatformDelegate : public PlatformDelegate {
+ public:
+ ~BasePlatformDelegate() override;
+
+ // PlatformDelegate:
+ bool PathIsReadable(const base::FilePath& file_path) const override;
+ bool DirectoryExists(const base::FilePath& file_path) const override;
+ FilePathMap<ExecutableMetadata> GetAllExecutableMetadata(
+ const FilePathSet& file_paths) override;
+
+ protected:
+ BasePlatformDelegate();
+
+ // Returns a map of file paths to whether a currently running process was
+ // spawned from that file. The set of file paths in the map are specified by
+ // `file_paths`.
+ FilePathMap<bool> AreExecutablesRunning(const FilePathSet& file_paths);
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_BASE_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/base_platform_delegate_unittest.cc b/chromium/components/device_signals/core/system_signals/base_platform_delegate_unittest.cc
new file mode 100644
index 00000000000..af048537b93
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/base_platform_delegate_unittest.cc
@@ -0,0 +1,55 @@
+// 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 "components/device_signals/core/system_signals/base_platform_delegate.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device_signals {
+
+namespace {
+
+constexpr base::FilePath::CharType kFileName[] = FILE_PATH_LITERAL("test_file");
+
+} // namespace
+
+class TestPlatformDelegate : public BasePlatformDelegate {
+ public:
+ TestPlatformDelegate() = default;
+ ~TestPlatformDelegate() override = default;
+
+ // Uninterested in pure virtual functions.
+ MOCK_METHOD(bool,
+ ResolveFilePath,
+ (const base::FilePath&, base::FilePath*),
+ (override));
+};
+
+class BasePlatformDelegateTest : public testing::Test {
+ protected:
+ BasePlatformDelegateTest() {
+ EXPECT_TRUE(scoped_dir_.CreateUniqueTempDir());
+ file_path_ = scoped_dir_.GetPath().Append(kFileName);
+ EXPECT_TRUE(base::WriteFile(file_path_, "irrelevant file content"));
+ }
+
+ base::ScopedTempDir scoped_dir_;
+ base::FilePath file_path_;
+ TestPlatformDelegate platform_delegate_;
+};
+
+// Sanity checks for functions that wrap base namespace functions. Only tests
+// success cases as underlying functions are tested on their own.
+TEST_F(BasePlatformDelegateTest, SanityChecks) {
+ EXPECT_TRUE(platform_delegate_.PathIsReadable(file_path_));
+ EXPECT_FALSE(platform_delegate_.DirectoryExists(file_path_));
+ EXPECT_TRUE(platform_delegate_.DirectoryExists(scoped_dir_.GetPath()));
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/file_system_service.cc b/chromium/components/device_signals/core/system_signals/file_system_service.cc
new file mode 100644
index 00000000000..d638a868ba6
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/file_system_service.cc
@@ -0,0 +1,133 @@
+// 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 "components/device_signals/core/system_signals/file_system_service.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/system_signals/hashing_utils.h"
+#include "components/device_signals/core/system_signals/platform_delegate.h"
+
+namespace device_signals {
+
+namespace {
+
+std::vector<FileSystemItem> GetAllItems(
+ const FilePathMap<std::vector<FileSystemItem>>& map) {
+ std::vector<FileSystemItem> items;
+ for (const auto& pair : map) {
+ items.insert(items.end(), pair.second.begin(), pair.second.end());
+ }
+ return items;
+}
+
+} // namespace
+
+class FileSystemServiceImpl : public FileSystemService {
+ public:
+ explicit FileSystemServiceImpl(std::unique_ptr<PlatformDelegate> delegate);
+ ~FileSystemServiceImpl() override;
+
+ // FileSystemService:
+ std::vector<FileSystemItem> GetSignals(
+ const std::vector<GetFileSystemInfoOptions>& options) override;
+ PresenceValue ResolveFileSystemItem(
+ const base::FilePath& original_file_path,
+ base::FilePath* resolved_file_path) const override;
+
+ private:
+ std::unique_ptr<PlatformDelegate> delegate_;
+};
+
+// static
+std::unique_ptr<FileSystemService> FileSystemService::Create(
+ std::unique_ptr<PlatformDelegate> delegate) {
+ return std::make_unique<FileSystemServiceImpl>(std::move(delegate));
+}
+
+FileSystemServiceImpl::FileSystemServiceImpl(
+ std::unique_ptr<PlatformDelegate> delegate)
+ : delegate_(std::move(delegate)) {}
+
+FileSystemServiceImpl::~FileSystemServiceImpl() = default;
+
+std::vector<FileSystemItem> FileSystemServiceImpl::GetSignals(
+ const std::vector<GetFileSystemInfoOptions>& options) {
+ // Keeping a map of resolved file paths to their corresponding
+ // FileSystemItem objects is required in order to call the batch
+ // GetAllExecutableMetadata API and having a way to map the resulting
+ // ExecutableMetadata back to the corresponding FileSystemItem.
+ // The value of the map is a vector since there is no guarantee that some of
+ // the resolved file paths don't point to the same file.
+ FilePathMap<std::vector<FileSystemItem>> resolved_paths_to_item_map;
+ FilePathSet executable_paths;
+ for (const auto& option : options) {
+ FileSystemItem collected_item;
+ collected_item.file_path = option.file_path;
+
+ // The resolved file path will be used internally for signal collection, but
+ // won't be returned to the caller.
+ base::FilePath resolved_file_path;
+ collected_item.presence =
+ ResolveFileSystemItem(option.file_path, &resolved_file_path);
+
+ // Only try to collect more signals if a file exists at the resolved path.
+ if (collected_item.presence == PresenceValue::kFound &&
+ !base::DirectoryExists(resolved_file_path)) {
+ if (option.compute_sha256) {
+ collected_item.sha256_hash = HashFile(resolved_file_path);
+ }
+
+ if (option.compute_executable_metadata) {
+ if (!executable_paths.contains(resolved_file_path)) {
+ executable_paths.insert(resolved_file_path);
+ }
+ }
+ }
+
+ if (!resolved_paths_to_item_map.contains(resolved_file_path)) {
+ resolved_paths_to_item_map[resolved_file_path] =
+ std::vector<FileSystemItem>();
+ }
+
+ resolved_paths_to_item_map[resolved_file_path].push_back(
+ std::move(collected_item));
+ }
+
+ auto collected_executable_metadata =
+ delegate_->GetAllExecutableMetadata(executable_paths);
+
+ for (const auto& path_metadata_pair : collected_executable_metadata) {
+ if (!resolved_paths_to_item_map.contains(path_metadata_pair.first)) {
+ continue;
+ }
+
+ for (auto& collected_item :
+ resolved_paths_to_item_map[path_metadata_pair.first]) {
+ collected_item.executable_metadata = path_metadata_pair.second;
+ }
+ }
+
+ return GetAllItems(resolved_paths_to_item_map);
+}
+
+PresenceValue FileSystemServiceImpl::ResolveFileSystemItem(
+ const base::FilePath& original_file_path,
+ base::FilePath* resolved_file_path) const {
+ base::FilePath local_resolved_path;
+ if (delegate_->ResolveFilePath(original_file_path, &local_resolved_path)) {
+ *resolved_file_path = local_resolved_path;
+ if (delegate_->PathIsReadable(local_resolved_path)) {
+ return PresenceValue::kFound;
+ }
+ return PresenceValue::kAccessDenied;
+ }
+ return PresenceValue::kNotFound;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/file_system_service.h b/chromium/components/device_signals/core/system_signals/file_system_service.h
new file mode 100644
index 00000000000..f6a7903831e
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/file_system_service.h
@@ -0,0 +1,45 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_FILE_SYSTEM_SERVICE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_FILE_SYSTEM_SERVICE_H_
+
+#include <memory>
+#include <vector>
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace device_signals {
+
+enum class PresenceValue;
+class PlatformDelegate;
+struct FileSystemItem;
+struct GetFileSystemInfoOptions;
+
+class FileSystemService {
+ public:
+ virtual ~FileSystemService() = default;
+
+ // Creates a FileSystemService instance using the given `delegate`.
+ static std::unique_ptr<FileSystemService> Create(
+ std::unique_ptr<PlatformDelegate> delegate);
+
+ // Collects and returns the file system items' signals as requested by
+ // `options`.
+ virtual std::vector<FileSystemItem> GetSignals(
+ const std::vector<GetFileSystemInfoOptions>& options) = 0;
+
+ // Given an `original_file_path`, will attempt to resolve any environment
+ // variables, and store that result in `resolved_file_path`, then calculate
+ // the PresenceValue of that file system resource and return that.
+ virtual PresenceValue ResolveFileSystemItem(
+ const base::FilePath& original_file_path,
+ base::FilePath* resolved_file_path) const = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_FILE_SYSTEM_SERVICE_H_
diff --git a/chromium/components/device_signals/core/system_signals/file_system_service_unittest.cc b/chromium/components/device_signals/core/system_signals/file_system_service_unittest.cc
new file mode 100644
index 00000000000..57fd867aab9
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/file_system_service_unittest.cc
@@ -0,0 +1,255 @@
+// 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 "components/device_signals/core/system_signals/file_system_service.h"
+
+#include <array>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/system_signals/mock_platform_delegate.h"
+#include "components/device_signals/core/system_signals/platform_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+
+namespace device_signals {
+
+namespace {
+
+GetFileSystemInfoOptions CreateOptions(const base::FilePath& path,
+ bool compute_sha256,
+ bool compute_executable_metadata) {
+ GetFileSystemInfoOptions options;
+ options.file_path = path;
+ options.compute_sha256 = compute_sha256;
+ options.compute_executable_metadata = compute_executable_metadata;
+ return options;
+}
+
+std::string HexEncodeHash(const std::string& hashed_data) {
+ return base::ToLowerASCII(
+ base::HexEncode(std::data(hashed_data), hashed_data.size()));
+}
+
+absl::optional<size_t> FindItemIndexByFilePath(
+ const base::FilePath& expected_file_path,
+ const std::vector<FileSystemItem>& items) {
+ for (size_t i = 0; i < items.size(); i++) {
+ if (items[i].file_path == expected_file_path) {
+ return i;
+ }
+ }
+ return absl::nullopt;
+}
+
+} // namespace
+
+class FileSystemServiceTest : public testing::Test {
+ protected:
+ FileSystemServiceTest() {
+ auto mock_platform_delegate =
+ std::make_unique<testing::StrictMock<MockPlatformDelegate>>();
+ mock_platform_delegate_ = mock_platform_delegate.get();
+
+ file_system_service_ =
+ FileSystemService::Create(std::move(mock_platform_delegate));
+
+ ON_CALL(*mock_platform_delegate_, GetAllExecutableMetadata(FilePathSet()))
+ .WillByDefault(Return(FilePathMap<ExecutableMetadata>()));
+ }
+
+ void ExpectResolvablePath(const base::FilePath& path,
+ const base::FilePath& resolved_path) {
+ EXPECT_CALL(*mock_platform_delegate_, ResolveFilePath(path, _))
+ .WillOnce(
+ Invoke([&resolved_path](const base::FilePath& original_file_path,
+ base::FilePath* resolved_file_path) {
+ *resolved_file_path = resolved_path;
+ return true;
+ }));
+ }
+
+ void ExpectPathIsReadable(const base::FilePath& path) {
+ EXPECT_CALL(*mock_platform_delegate_, PathIsReadable(path))
+ .WillOnce(Return(true));
+ }
+
+ testing::StrictMock<MockPlatformDelegate>* mock_platform_delegate_;
+ std::unique_ptr<FileSystemService> file_system_service_;
+};
+
+// Tests all possible PresenceValue outcomes.
+TEST_F(FileSystemServiceTest, GetSignals_Presence) {
+ base::FilePath unresolvable_file_path =
+ base::FilePath::FromUTF8Unsafe("/cannot/resolve");
+ EXPECT_CALL(*mock_platform_delegate_,
+ ResolveFilePath(unresolvable_file_path, _))
+ .WillOnce(Return(false));
+
+ base::FilePath access_denied_path =
+ base::FilePath::FromUTF8Unsafe("/cannot/access");
+ base::FilePath access_denied_path_resolved =
+ base::FilePath::FromUTF8Unsafe("/cannot/access/resolved");
+ ExpectResolvablePath(access_denied_path, access_denied_path_resolved);
+ EXPECT_CALL(*mock_platform_delegate_,
+ PathIsReadable(access_denied_path_resolved))
+ .WillOnce(Return(false));
+
+ base::FilePath found_path = base::FilePath::FromUTF8Unsafe("/found");
+ base::FilePath found_path_resolved =
+ base::FilePath::FromUTF8Unsafe("/found/resolved");
+ ExpectResolvablePath(found_path, found_path_resolved);
+ ExpectPathIsReadable(found_path_resolved);
+
+ std::vector<GetFileSystemInfoOptions> options;
+ options.push_back(CreateOptions(unresolvable_file_path, true, false));
+ options.push_back(CreateOptions(access_denied_path, true, false));
+ options.push_back(CreateOptions(found_path, false, false));
+
+ std::array<PresenceValue, 4> expected_presence_values{
+ PresenceValue::kNotFound, PresenceValue::kAccessDenied,
+ PresenceValue::kFound};
+
+ EXPECT_CALL(*mock_platform_delegate_,
+ GetAllExecutableMetadata(FilePathSet()));
+
+ auto file_system_items = file_system_service_->GetSignals(options);
+
+ ASSERT_EQ(file_system_items.size(), options.size());
+
+ for (size_t i = 0; i < options.size(); i++) {
+ auto index =
+ FindItemIndexByFilePath(options[i].file_path, file_system_items);
+ ASSERT_TRUE(index.has_value());
+
+ const FileSystemItem& item = file_system_items[index.value()];
+ EXPECT_EQ(item.presence, expected_presence_values[i]);
+
+ // No metadata was collected, as it was only requested for the two files
+ // that cannot be found/accessed.
+ EXPECT_FALSE(item.sha256_hash.has_value());
+ EXPECT_FALSE(item.executable_metadata.has_value());
+ }
+}
+
+// Tests calculating the hash for a file when requested, but not for a
+// directory.
+TEST_F(FileSystemServiceTest, GetSignals_Hash_Success) {
+ base::ScopedTempDir scoped_dir;
+ ASSERT_TRUE(scoped_dir.CreateUniqueTempDir());
+
+ constexpr base::FilePath::CharType existing_file[] =
+ FILE_PATH_LITERAL("existing_file");
+
+ const base::FilePath absolute_file_path =
+ scoped_dir.GetPath().Append(existing_file);
+
+ // Write some content.
+ ASSERT_TRUE(base::WriteFile(absolute_file_path, "some file content"));
+
+ ExpectResolvablePath(absolute_file_path, absolute_file_path);
+ ExpectPathIsReadable(absolute_file_path);
+ ExpectResolvablePath(scoped_dir.GetPath(), scoped_dir.GetPath());
+ ExpectPathIsReadable(scoped_dir.GetPath());
+
+ std::vector<GetFileSystemInfoOptions> options;
+ options.push_back(
+ CreateOptions(absolute_file_path, /*compute_sha256=*/true, false));
+ options.push_back(
+ CreateOptions(scoped_dir.GetPath(), /*compute_sha256=*/true, false));
+
+ EXPECT_CALL(*mock_platform_delegate_,
+ GetAllExecutableMetadata(FilePathSet()));
+
+ auto file_system_items = file_system_service_->GetSignals(options);
+
+ constexpr char expected_sha256_hash[] =
+ "b05ffa4eea8fb5609d576a68c1066be3f99e4dc53d365a0ac2a78259b2dd91f9";
+ ASSERT_EQ(file_system_items.size(), options.size());
+
+ // The file's hash was computed.
+ auto index = FindItemIndexByFilePath(absolute_file_path, file_system_items);
+ ASSERT_TRUE(index.has_value());
+ FileSystemItem& item = file_system_items[index.value()];
+ EXPECT_EQ(item.presence, PresenceValue::kFound);
+ ASSERT_TRUE(item.sha256_hash.has_value());
+ EXPECT_EQ(HexEncodeHash(item.sha256_hash.value()), expected_sha256_hash);
+
+ // The directory does not have a hash.
+ index = FindItemIndexByFilePath(scoped_dir.GetPath(), file_system_items);
+ ASSERT_TRUE(index.has_value());
+ item = file_system_items[index.value()];
+ EXPECT_EQ(item.file_path, scoped_dir.GetPath());
+ EXPECT_EQ(item.presence, PresenceValue::kFound);
+ EXPECT_FALSE(item.sha256_hash.has_value());
+}
+
+// Tests that the FileSystemService uses the PlatformDelegate to collect
+// the ExecutableMetadata only when requested.
+TEST_F(FileSystemServiceTest, GetSignals_ExecutableMetadata) {
+ base::FilePath first_found_path =
+ base::FilePath::FromUTF8Unsafe("/first/found");
+ base::FilePath first_found_path_resolved =
+ base::FilePath::FromUTF8Unsafe("/first/found/resolved");
+ ExpectResolvablePath(first_found_path, first_found_path_resolved);
+ ExpectPathIsReadable(first_found_path_resolved);
+
+ base::FilePath second_found_path =
+ base::FilePath::FromUTF8Unsafe("/second/found");
+ base::FilePath second_found_path_resolved =
+ base::FilePath::FromUTF8Unsafe("/second/found/resolved");
+ ExpectResolvablePath(second_found_path, second_found_path_resolved);
+ ExpectPathIsReadable(second_found_path_resolved);
+
+ ExecutableMetadata executable_metadata;
+ executable_metadata.is_running = true;
+
+ FilePathSet expected_path_set;
+ expected_path_set.insert(first_found_path_resolved);
+
+ FilePathMap<ExecutableMetadata> result_metadata_map;
+ result_metadata_map.insert({first_found_path_resolved, executable_metadata});
+
+ EXPECT_CALL(*mock_platform_delegate_,
+ GetAllExecutableMetadata(expected_path_set))
+ .WillOnce(Return(result_metadata_map));
+
+ std::vector<GetFileSystemInfoOptions> options;
+ options.push_back(CreateOptions(first_found_path, false,
+ /*compute_executable_metadata=*/true));
+ options.push_back(CreateOptions(second_found_path, false,
+ /*compute_executable_metadata=*/false));
+
+ auto file_system_items = file_system_service_->GetSignals(options);
+
+ ASSERT_EQ(file_system_items.size(), options.size());
+
+ // The first file's executable metadata was properly retrieved via the
+ // mocked delegate.
+ auto index = FindItemIndexByFilePath(first_found_path, file_system_items);
+ ASSERT_TRUE(index.has_value());
+ FileSystemItem& item = file_system_items[index.value()];
+ EXPECT_EQ(item.presence, PresenceValue::kFound);
+ ASSERT_TRUE(item.executable_metadata.has_value());
+ EXPECT_EQ(item.executable_metadata.value(), executable_metadata);
+
+ // We did not request executable metadata from the second file, so it should
+ // be absl::nullopt.
+ index = FindItemIndexByFilePath(second_found_path, file_system_items);
+ ASSERT_TRUE(index.has_value());
+ item = file_system_items[index.value()];
+ EXPECT_EQ(item.presence, PresenceValue::kFound);
+ EXPECT_FALSE(item.executable_metadata.has_value());
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/hashing_utils.cc b/chromium/components/device_signals/core/system_signals/hashing_utils.cc
new file mode 100644
index 00000000000..3fd2ba6a438
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/hashing_utils.cc
@@ -0,0 +1,48 @@
+// 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 "components/device_signals/core/system_signals/hashing_utils.h"
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/memory/page_size.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+
+namespace device_signals {
+
+absl::optional<std::string> HashFile(const base::FilePath& file_path) {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+
+ base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid()) {
+ return absl::nullopt;
+ }
+
+ auto secure_hash =
+ crypto::SecureHash::Create(crypto::SecureHash::Algorithm::SHA256);
+ std::vector<char> buffer(base::GetPageSize());
+
+ int bytes_read = 0;
+ do {
+ bytes_read = file.ReadAtCurrentPos(buffer.data(), buffer.size());
+ if (bytes_read == -1) {
+ return absl::nullopt;
+ }
+ secure_hash->Update(buffer.data(), bytes_read);
+ } while (bytes_read > 0);
+
+ std::string hash(crypto::kSHA256Length, 0);
+ secure_hash->Finish(std::data(hash), hash.size());
+ return hash;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/hashing_utils.h b/chromium/components/device_signals/core/system_signals/hashing_utils.h
new file mode 100644
index 00000000000..92c7fee99e4
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/hashing_utils.h
@@ -0,0 +1,24 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_HASHING_UTILS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_HASHING_UTILS_H_
+
+#include <string>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace device_signals {
+
+// Tries to generate a byte string containing the SHA256 value of the file at
+// `file_path`. Will return absl::nullopt in invalid cases.
+absl::optional<std::string> HashFile(const base::FilePath& file_path);
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_HASHING_UTILS_H_
diff --git a/chromium/components/device_signals/core/system_signals/linux/BUILD.gn b/chromium/components/device_signals/core/system_signals/linux/BUILD.gn
new file mode 100644
index 00000000000..a8b4da3765c
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/linux/BUILD.gn
@@ -0,0 +1,17 @@
+# 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.
+
+static_library("linux") {
+ public = [ "linux_platform_delegate.h" ]
+
+ sources = [ "linux_platform_delegate.cc" ]
+
+ public_deps = [
+ "//components/device_signals/core/common",
+ "//components/device_signals/core/system_signals",
+ "//components/device_signals/core/system_signals/posix",
+ ]
+
+ deps = [ "//base" ]
+}
diff --git a/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.cc b/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.cc
new file mode 100644
index 00000000000..a4e4da312cb
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.cc
@@ -0,0 +1,13 @@
+// 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 "components/device_signals/core/system_signals/linux/linux_platform_delegate.h"
+
+namespace device_signals {
+
+LinuxPlatformDelegate::LinuxPlatformDelegate() = default;
+
+LinuxPlatformDelegate::~LinuxPlatformDelegate() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.h b/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.h
new file mode 100644
index 00000000000..9fb85f3b644
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/linux/linux_platform_delegate.h
@@ -0,0 +1,20 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_LINUX_LINUX_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_LINUX_LINUX_PLATFORM_DELEGATE_H_
+
+#include "components/device_signals/core/system_signals/posix/posix_platform_delegate.h"
+
+namespace device_signals {
+
+class LinuxPlatformDelegate : public PosixPlatformDelegate {
+ public:
+ LinuxPlatformDelegate();
+ ~LinuxPlatformDelegate() override;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_LINUX_LINUX_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/mac/BUILD.gn b/chromium/components/device_signals/core/system_signals/mac/BUILD.gn
new file mode 100644
index 00000000000..a9fe889fad6
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mac/BUILD.gn
@@ -0,0 +1,17 @@
+# 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.
+
+static_library("mac") {
+ public = [ "mac_platform_delegate.h" ]
+
+ sources = [ "mac_platform_delegate.mm" ]
+
+ public_deps = [
+ "//components/device_signals/core/common",
+ "//components/device_signals/core/system_signals",
+ "//components/device_signals/core/system_signals/posix",
+ ]
+
+ deps = [ "//base" ]
+}
diff --git a/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.h b/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.h
new file mode 100644
index 00000000000..2bc1887e658
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.h
@@ -0,0 +1,20 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MAC_MAC_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MAC_MAC_PLATFORM_DELEGATE_H_
+
+#include "components/device_signals/core/system_signals/posix/posix_platform_delegate.h"
+
+namespace device_signals {
+
+class MacPlatformDelegate : public PosixPlatformDelegate {
+ public:
+ MacPlatformDelegate();
+ ~MacPlatformDelegate() override;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MAC_MAC_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.mm b/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.mm
new file mode 100644
index 00000000000..e763e18bad5
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mac/mac_platform_delegate.mm
@@ -0,0 +1,13 @@
+// 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 "components/device_signals/core/system_signals/mac/mac_platform_delegate.h"
+
+namespace device_signals {
+
+MacPlatformDelegate::MacPlatformDelegate() = default;
+
+MacPlatformDelegate::~MacPlatformDelegate() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/mock_file_system_service.cc b/chromium/components/device_signals/core/system_signals/mock_file_system_service.cc
new file mode 100644
index 00000000000..0285aa5e64a
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mock_file_system_service.cc
@@ -0,0 +1,12 @@
+// 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 "components/device_signals/core/system_signals/mock_file_system_service.h"
+
+namespace device_signals {
+
+MockFileSystemService::MockFileSystemService() = default;
+MockFileSystemService::~MockFileSystemService() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/mock_file_system_service.h b/chromium/components/device_signals/core/system_signals/mock_file_system_service.h
new file mode 100644
index 00000000000..3b6badfc228
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mock_file_system_service.h
@@ -0,0 +1,32 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_FILE_SYSTEM_SERVICE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_FILE_SYSTEM_SERVICE_H_
+
+#include "components/device_signals/core/system_signals/file_system_service.h"
+
+#include "components/device_signals/core/common/common_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockFileSystemService : public FileSystemService {
+ public:
+ MockFileSystemService();
+ ~MockFileSystemService() override;
+
+ MOCK_METHOD(std::vector<FileSystemItem>,
+ GetSignals,
+ (const std::vector<GetFileSystemInfoOptions>&),
+ (override));
+ MOCK_METHOD(PresenceValue,
+ ResolveFileSystemItem,
+ (const base::FilePath&, base::FilePath*),
+ (const override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_FILE_SYSTEM_SERVICE_H_
diff --git a/chromium/components/device_signals/core/system_signals/mock_platform_delegate.cc b/chromium/components/device_signals/core/system_signals/mock_platform_delegate.cc
new file mode 100644
index 00000000000..c15adacec80
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mock_platform_delegate.cc
@@ -0,0 +1,12 @@
+// 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 "components/device_signals/core/system_signals/mock_platform_delegate.h"
+
+namespace device_signals {
+
+MockPlatformDelegate::MockPlatformDelegate() = default;
+MockPlatformDelegate::~MockPlatformDelegate() = default;
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/mock_platform_delegate.h b/chromium/components/device_signals/core/system_signals/mock_platform_delegate.h
new file mode 100644
index 00000000000..a08ed546ad1
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/mock_platform_delegate.h
@@ -0,0 +1,34 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_PLATFORM_DELEGATE_H_
+
+#include "base/files/file_path.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "components/device_signals/core/system_signals/platform_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockPlatformDelegate : public PlatformDelegate {
+ public:
+ MockPlatformDelegate();
+ ~MockPlatformDelegate() override;
+
+ MOCK_METHOD(bool, PathIsReadable, (const base::FilePath&), (const override));
+ MOCK_METHOD(bool, DirectoryExists, (const base::FilePath&), (const override));
+ MOCK_METHOD(bool,
+ ResolveFilePath,
+ (const base::FilePath&, base::FilePath*),
+ (override));
+ MOCK_METHOD(FilePathMap<ExecutableMetadata>,
+ GetAllExecutableMetadata,
+ (const FilePathSet&),
+ (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_MOCK_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/platform_delegate.cc b/chromium/components/device_signals/core/system_signals/platform_delegate.cc
new file mode 100644
index 00000000000..973ce713608
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/platform_delegate.cc
@@ -0,0 +1,23 @@
+// 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 "components/device_signals/core/system_signals/platform_delegate.h"
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+
+namespace device_signals {
+
+bool CustomFilePathComparator::operator()(const base::FilePath& a,
+ const base::FilePath& b) const {
+#if BUILDFLAG(IS_LINUX)
+ // On Linux, the file system is case sensitive.
+ return a < b;
+#else
+ // On Windows and Mac, the file system is case insensitive.
+ return base::FilePath::CompareLessIgnoreCase(a.value(), b.value());
+#endif
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/platform_delegate.h b/chromium/components/device_signals/core/system_signals/platform_delegate.h
new file mode 100644
index 00000000000..3cc0ed15894
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/platform_delegate.h
@@ -0,0 +1,52 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_DELEGATE_H_
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace device_signals {
+
+struct ExecutableMetadata;
+
+struct CustomFilePathComparator {
+ bool operator()(const base::FilePath& a, const base::FilePath& b) const;
+};
+
+template <typename T>
+using FilePathMap = base::flat_map<base::FilePath, T, CustomFilePathComparator>;
+
+using FilePathSet = base::flat_set<base::FilePath, CustomFilePathComparator>;
+
+// Interface whose derived types encapsulate OS-specific functionalities.
+class PlatformDelegate {
+ public:
+ virtual ~PlatformDelegate() = default;
+
+ // Wrapper functions around implementation in base/files/file_util.h to allow
+ // mocking in tests.
+ virtual bool PathIsReadable(const base::FilePath& file_path) const = 0;
+ virtual bool DirectoryExists(const base::FilePath& file_path) const = 0;
+
+ // Resolves environment variables and relative markers in `file_path`, and
+ // returns the absolute path via `resolved_file_path`. Returns true if
+ // successful. For consistency on all platforms, this method will return false
+ // if no file system item resides at the end path.
+ virtual bool ResolveFilePath(const base::FilePath& file_path,
+ base::FilePath* resolved_file_path) = 0;
+
+ // Collects and return executable metadata for all the files in `file_paths`.
+ virtual FilePathMap<ExecutableMetadata> GetAllExecutableMetadata(
+ const FilePathSet& file_paths) = 0;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/platform_utils.h b/chromium/components/device_signals/core/system_signals/platform_utils.h
new file mode 100644
index 00000000000..9152e54d0af
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/platform_utils.h
@@ -0,0 +1,23 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_UTILS_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_UTILS_H_
+
+#include "base/process/process_handle.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace device_signals {
+
+// Returns the file path pointing to the executable file that spawned
+// the given process `pid`.
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid);
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_PLATFORM_UTILS_H_
diff --git a/chromium/components/device_signals/core/system_signals/posix/BUILD.gn b/chromium/components/device_signals/core/system_signals/posix/BUILD.gn
new file mode 100644
index 00000000000..513f71a302f
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/posix/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+static_library("posix") {
+ public = [ "posix_platform_delegate.h" ]
+
+ sources = [ "posix_platform_delegate.cc" ]
+
+ public_deps = [
+ "//components/device_signals/core/common",
+ "//components/device_signals/core/system_signals",
+ ]
+
+ deps = [ "//base" ]
+}
diff --git a/chromium/components/device_signals/core/system_signals/posix/platform_utils_posix.cc b/chromium/components/device_signals/core/system_signals/posix/platform_utils_posix.cc
new file mode 100644
index 00000000000..e749bdb4fe9
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/posix/platform_utils_posix.cc
@@ -0,0 +1,20 @@
+// 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 "components/device_signals/core/system_signals/platform_utils.h"
+
+#include "base/files/file_path.h"
+#include "base/process/process_handle.h"
+
+namespace device_signals {
+
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
+ auto file_path = base::GetProcessExecutablePath(pid);
+ if (file_path.empty()) {
+ return absl::nullopt;
+ }
+ return file_path;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc b/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc
new file mode 100644
index 00000000000..0b22a56febe
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.cc
@@ -0,0 +1,30 @@
+// 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 "components/device_signals/core/system_signals/posix/posix_platform_delegate.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "components/device_signals/core/common/common_types.h"
+
+namespace device_signals {
+
+PosixPlatformDelegate::PosixPlatformDelegate() = default;
+
+PosixPlatformDelegate::~PosixPlatformDelegate() = default;
+
+bool PosixPlatformDelegate::ResolveFilePath(
+ const base::FilePath& file_path,
+ base::FilePath* resolved_file_path) {
+ base::FilePath local_resolved_file_path =
+ base::MakeAbsoluteFilePath(file_path);
+ if (local_resolved_file_path.empty()) {
+ return false;
+ }
+
+ *resolved_file_path = local_resolved_file_path;
+ return true;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.h b/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.h
new file mode 100644
index 00000000000..feca4766532
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/posix/posix_platform_delegate.h
@@ -0,0 +1,26 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_POSIX_POSIX_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_POSIX_POSIX_PLATFORM_DELEGATE_H_
+
+#include "components/device_signals/core/system_signals/base_platform_delegate.h"
+
+namespace device_signals {
+
+class PosixPlatformDelegate : public BasePlatformDelegate {
+ public:
+ ~PosixPlatformDelegate() override;
+
+ // PlatformDelegate:
+ bool ResolveFilePath(const base::FilePath& file_path,
+ base::FilePath* resolved_file_path) override;
+
+ protected:
+ PosixPlatformDelegate();
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_POSIX_POSIX_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/win/BUILD.gn b/chromium/components/device_signals/core/system_signals/win/BUILD.gn
new file mode 100644
index 00000000000..9c81e5b7d55
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/BUILD.gn
@@ -0,0 +1,68 @@
+# 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.
+
+static_library("win") {
+ public = [
+ "win_platform_delegate.h",
+ "wmi_client.h",
+ "wmi_client_impl.h",
+ "wsc_client.h",
+ "wsc_client_impl.h",
+ ]
+
+ sources = [
+ "win_platform_delegate.cc",
+ "wmi_client.cc",
+ "wmi_client_impl.cc",
+ "wsc_client.cc",
+ "wsc_client_impl.cc",
+ ]
+
+ public_deps = [
+ "//base",
+ "//components/device_signals/core/common",
+ "//components/device_signals/core/common/win",
+ "//components/device_signals/core/system_signals",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "com_fakes.cc",
+ "com_fakes.h",
+ "mock_wmi_client.cc",
+ "mock_wmi_client.h",
+ "mock_wsc_client.cc",
+ "mock_wsc_client.h",
+ ]
+
+ deps = [
+ ":win",
+ "//base",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "win_platform_delegate_unittest.cc",
+ "wmi_client_impl_unittest.cc",
+ "wsc_client_impl_unittest.cc",
+ ]
+
+ deps = [
+ ":test_support",
+ ":win",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/abseil-cpp:absl",
+ ]
+}
diff --git a/chromium/components/device_signals/core/common/win/com_fakes.cc b/chromium/components/device_signals/core/system_signals/win/com_fakes.cc
index b9c74e9ad30..3d1c01b91be 100644
--- a/chromium/components/device_signals/core/common/win/com_fakes.cc
+++ b/chromium/components/device_signals/core/system_signals/win/com_fakes.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/com_fakes.h"
+#include "components/device_signals/core/system_signals/win/com_fakes.h"
#include "base/check.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/device_signals/core/common/win/com_fakes.h b/chromium/components/device_signals/core/system_signals/win/com_fakes.h
index 06fc7314b75..5bbfd286142 100644
--- a/chromium/components/device_signals/core/common/win/com_fakes.h
+++ b/chromium/components/device_signals/core/system_signals/win/com_fakes.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_COM_FAKES_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_COM_FAKES_H_
#include <atlcomcli.h>
#include <iwscapi.h>
@@ -231,4 +231,4 @@ class FakeWSCProductList : public IWSCProductList {
} // namespace device_signals
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_COM_FAKES_H_
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_COM_FAKES_H_
diff --git a/chromium/components/device_signals/core/common/win/mock_wmi_client.cc b/chromium/components/device_signals/core/system_signals/win/mock_wmi_client.cc
index f12f35b0c60..f7f3729dad3 100644
--- a/chromium/components/device_signals/core/common/win/mock_wmi_client.cc
+++ b/chromium/components/device_signals/core/system_signals/win/mock_wmi_client.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/mock_wmi_client.h"
+#include "components/device_signals/core/system_signals/win/mock_wmi_client.h"
namespace device_signals {
diff --git a/chromium/components/device_signals/core/system_signals/win/mock_wmi_client.h b/chromium/components/device_signals/core/system_signals/win/mock_wmi_client.h
new file mode 100644
index 00000000000..500526e08c5
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/mock_wmi_client.h
@@ -0,0 +1,24 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WMI_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WMI_CLIENT_H_
+
+#include "components/device_signals/core/system_signals/win/wmi_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device_signals {
+
+class MockWmiClient : public WmiClient {
+ public:
+ MockWmiClient();
+ ~MockWmiClient() override;
+
+ MOCK_METHOD(WmiAvProductsResponse, GetAntiVirusProducts, (), (override));
+ MOCK_METHOD(WmiHotfixesResponse, GetInstalledHotfixes, (), (override));
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/mock_wsc_client.cc b/chromium/components/device_signals/core/system_signals/win/mock_wsc_client.cc
index 18bb1698827..c0c75d4552e 100644
--- a/chromium/components/device_signals/core/common/win/mock_wsc_client.cc
+++ b/chromium/components/device_signals/core/system_signals/win/mock_wsc_client.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/mock_wsc_client.h"
+#include "components/device_signals/core/system_signals/win/mock_wsc_client.h"
namespace device_signals {
diff --git a/chromium/components/device_signals/core/common/win/mock_wsc_client.h b/chromium/components/device_signals/core/system_signals/win/mock_wsc_client.h
index 080dfa50fbe..77267617e07 100644
--- a/chromium/components/device_signals/core/common/win/mock_wsc_client.h
+++ b/chromium/components/device_signals/core/system_signals/win/mock_wsc_client.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WSC_CLIENT_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WSC_CLIENT_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WSC_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WSC_CLIENT_H_
-#include "components/device_signals/core/common/win/wsc_client.h"
+#include "components/device_signals/core/system_signals/win/wsc_client.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace device_signals {
@@ -20,4 +20,4 @@ class MockWscClient : public WscClient {
} // namespace device_signals
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_MOCK_WMI_CLIENT_H_
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_MOCK_WSC_CLIENT_H_
diff --git a/chromium/components/device_signals/core/system_signals/win/platform_utils_win.cc b/chromium/components/device_signals/core/system_signals/win/platform_utils_win.cc
new file mode 100644
index 00000000000..c25fff49d85
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/platform_utils_win.cc
@@ -0,0 +1,31 @@
+// 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 "components/device_signals/core/system_signals/platform_utils.h"
+
+#include <windows.h>
+
+#include "base/files/file_path.h"
+#include "base/process/process.h"
+
+namespace device_signals {
+
+absl::optional<base::FilePath> GetProcessExePath(base::ProcessId pid) {
+ base::Process process(
+ ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid));
+ if (!process.IsValid()) {
+ return absl::nullopt;
+ }
+
+ DWORD path_len = MAX_PATH;
+ wchar_t path_string[MAX_PATH];
+ if (!::QueryFullProcessImageName(process.Handle(), 0, path_string,
+ &path_len)) {
+ return absl::nullopt;
+ }
+
+ return base::FilePath(path_string);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.cc b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.cc
new file mode 100644
index 00000000000..dcf1d119d58
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.cc
@@ -0,0 +1,65 @@
+// 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 "components/device_signals/core/system_signals/win/win_platform_delegate.h"
+
+#include <windows.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/process/process.h"
+#include "base/process/process_iterator.h"
+#include "base/strings/string_util.h"
+#include "base/strings/string_util_win.h"
+#include "components/device_signals/core/common/common_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace device_signals {
+
+namespace {
+
+// Helper function for expanding all environment variables in `path`.
+absl::optional<std::wstring> ExpandEnvironmentVariables(
+ const std::wstring& path) {
+ static const DWORD kMaxBuffer = 32 * 1024; // Max according to MSDN.
+ std::wstring path_expanded;
+ DWORD path_len = MAX_PATH;
+ do {
+ DWORD result = ::ExpandEnvironmentStrings(
+ path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
+ if (!result) {
+ // Failed to expand variables.
+ break;
+ }
+ if (result <= path_len)
+ return path_expanded.substr(0, result - 1);
+ path_len = result;
+ } while (path_len < kMaxBuffer);
+
+ return absl::nullopt;
+}
+
+} // namespace
+
+WinPlatformDelegate::WinPlatformDelegate() = default;
+
+WinPlatformDelegate::~WinPlatformDelegate() = default;
+
+bool WinPlatformDelegate::ResolveFilePath(const base::FilePath& file_path,
+ base::FilePath* resolved_file_path) {
+ auto expanded_path_wstring = ExpandEnvironmentVariables(file_path.value());
+ if (!expanded_path_wstring) {
+ return false;
+ }
+
+ auto expanded_file_path = base::FilePath(expanded_path_wstring.value());
+ if (!base::PathExists(expanded_file_path)) {
+ return false;
+ }
+
+ *resolved_file_path = base::MakeAbsoluteFilePath(expanded_file_path);
+ return true;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.h b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.h
new file mode 100644
index 00000000000..b8e456a20c5
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate.h
@@ -0,0 +1,24 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WIN_PLATFORM_DELEGATE_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WIN_PLATFORM_DELEGATE_H_
+
+#include "components/device_signals/core/system_signals/base_platform_delegate.h"
+
+namespace device_signals {
+
+class WinPlatformDelegate : public BasePlatformDelegate {
+ public:
+ WinPlatformDelegate();
+ ~WinPlatformDelegate() override;
+
+ // PlatformDelegate:
+ bool ResolveFilePath(const base::FilePath& file_path,
+ base::FilePath* resolved_file_path) override;
+};
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WIN_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
new file mode 100644
index 00000000000..8e6f82a075c
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
@@ -0,0 +1,80 @@
+// 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 "components/device_signals/core/system_signals/win/win_platform_delegate.h"
+
+#include <array>
+
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device_signals {
+
+namespace {
+
+// Using regular strings instead of file path literals as they will be used
+// to construct all sorts of file paths, and also non-file-paths.
+constexpr char kEnvironmentVariableName[] = "TestEnvironmentVariablePath";
+constexpr char kTestFileName[] = "test_file";
+
+constexpr base::FilePath::CharType kInexistantFileName[] =
+ FILE_PATH_LITERAL("does_not_exit");
+
+} // namespace
+
+class WinPlatformDelegateTest : public testing::Test {
+ protected:
+ WinPlatformDelegateTest() : env_(base::Environment::Create()) {
+ EXPECT_TRUE(scoped_dir_.CreateUniqueTempDir());
+ absolute_file_path_ = scoped_dir_.GetPath().Append(
+ base::FilePath::FromUTF8Unsafe(kTestFileName));
+ EXPECT_TRUE(
+ base::WriteFile(absolute_file_path_, "irrelevant file content"));
+
+ env_->SetVar(kEnvironmentVariableName,
+ scoped_dir_.GetPath().AsUTF8Unsafe());
+ }
+
+ ~WinPlatformDelegateTest() override {
+ env_->UnSetVar(kEnvironmentVariableName);
+ }
+
+ base::ScopedTempDir scoped_dir_;
+ base::FilePath absolute_file_path_;
+ std::unique_ptr<base::Environment> env_;
+ WinPlatformDelegate platform_delegate_;
+};
+
+TEST_F(WinPlatformDelegateTest, ResolveFilePath_Success) {
+ std::string directory_name = scoped_dir_.GetPath().BaseName().AsUTF8Unsafe();
+
+ std::array<std::string, 4> test_cases = {
+ absolute_file_path_.AsUTF8Unsafe(),
+ base::StrCat({"%", kEnvironmentVariableName, "%\\", kTestFileName}),
+ base::StrCat({"%", kEnvironmentVariableName, "%\\..\\", directory_name,
+ "\\", kTestFileName}),
+
+ // Should work with directories too.
+ scoped_dir_.GetPath().AsUTF8Unsafe()};
+
+ for (const auto& test_case : test_cases) {
+ base::FilePath resolved_fp;
+ EXPECT_TRUE(platform_delegate_.ResolveFilePath(
+ base::FilePath::FromUTF8Unsafe(test_case), &resolved_fp));
+ }
+}
+
+TEST_F(WinPlatformDelegateTest, ResolveFilePath_Fail) {
+ base::FilePath resolved_fp;
+ EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+ scoped_dir_.GetPath().Append(kInexistantFileName), &resolved_fp));
+ EXPECT_EQ(resolved_fp, base::FilePath());
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wmi_client.cc b/chromium/components/device_signals/core/system_signals/win/wmi_client.cc
index 7aa439e8792..f460779d8dc 100644
--- a/chromium/components/device_signals/core/common/win/wmi_client.cc
+++ b/chromium/components/device_signals/core/system_signals/win/wmi_client.cc
@@ -1,11 +1,18 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/wmi_client.h"
+#include "components/device_signals/core/system_signals/win/wmi_client.h"
namespace device_signals {
+WmiAvProductsResponse::WmiAvProductsResponse() = default;
+
+WmiAvProductsResponse::WmiAvProductsResponse(
+ const WmiAvProductsResponse& other) = default;
+
+WmiAvProductsResponse::~WmiAvProductsResponse() = default;
+
WmiHotfixesResponse::WmiHotfixesResponse() = default;
WmiHotfixesResponse::WmiHotfixesResponse(const WmiHotfixesResponse& other) =
diff --git a/chromium/components/device_signals/core/common/win/wmi_client.h b/chromium/components/device_signals/core/system_signals/win/wmi_client.h
index 6215633cf7a..58fb426093a 100644
--- a/chromium/components/device_signals/core/common/win/wmi_client.h
+++ b/chromium/components/device_signals/core/system_signals/win/wmi_client.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_H_
#include <string>
#include <vector>
@@ -17,10 +17,28 @@
namespace device_signals {
// Errors that can occur when calling WMI, or parsing response values.
+// Do not change ordering. This enum is captured as
+// `DeviceSignalsWmiParsingError` in enums.xml.
enum class WmiParsingError {
kFailedToIterateResults = 0,
kFailedToGetName = 1,
- kMaxValue = kFailedToGetName
+ kFailedToGetState = 2,
+ kStateInvalid = 3,
+ kFailedToGetId = 4,
+ kMaxValue = kFailedToGetId
+};
+
+// Response object for calls to retrieve information about installed AntiVirus
+// software.
+struct WmiAvProductsResponse {
+ WmiAvProductsResponse();
+ ~WmiAvProductsResponse();
+
+ WmiAvProductsResponse(const WmiAvProductsResponse& other);
+
+ std::vector<AvProduct> av_products;
+ absl::optional<base::win::WmiError> query_error;
+ std::vector<WmiParsingError> parsing_errors;
};
// Response object for calls to retrieve information about installed hotfix
@@ -41,10 +59,13 @@ class WmiClient {
public:
virtual ~WmiClient() = default;
+ // Will retrieve information about installed AntiVirus software.
+ virtual WmiAvProductsResponse GetAntiVirusProducts() = 0;
+
// Will retrieve information about installed hotfix updates.
virtual WmiHotfixesResponse GetInstalledHotfixes() = 0;
};
} // namespace device_signals
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WMI_CLIENT_H_
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_H_
diff --git a/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.cc b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.cc
new file mode 100644
index 00000000000..779558b84a4
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.cc
@@ -0,0 +1,201 @@
+// 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 "components/device_signals/core/system_signals/win/wmi_client_impl.h"
+
+#include <wbemidl.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/win/com_init_util.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
+#include "base/win/wmi.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+namespace {
+
+// Parses a string value from `class_object` named `property_name`.
+absl::optional<std::string> ParseString(
+ const std::wstring& property_name,
+ const ComPtr<IWbemClassObject>& class_object) {
+ base::win::ScopedVariant string_variant;
+ HRESULT hr = class_object->Get(property_name.c_str(), 0,
+ string_variant.Receive(), 0, 0);
+
+ if (FAILED(hr) || string_variant.type() != VT_BSTR) {
+ return absl::nullopt;
+ }
+
+ // Owned by ScopedVariant.
+ BSTR temp_bstr = V_BSTR(string_variant.ptr());
+ return base::SysWideToUTF8(
+ std::wstring(temp_bstr, ::SysStringLen(temp_bstr)));
+}
+
+} // namespace
+
+WmiClientImpl::WmiClientImpl()
+ : run_query_callback_(base::BindRepeating(base::win::RunWmiQuery)) {}
+
+WmiClientImpl::WmiClientImpl(RunWmiQueryCallback run_query_callback)
+ : run_query_callback_(std::move(run_query_callback)) {}
+
+WmiClientImpl::~WmiClientImpl() = default;
+
+WmiAvProductsResponse WmiClientImpl::GetAntiVirusProducts() {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+ ComPtr<IEnumWbemClassObject> enumerator;
+ auto error_code =
+ run_query_callback_.Run(base::win::kSecurityCenter2ServerName,
+ L"SELECT * FROM AntiVirusProduct", &enumerator);
+
+ WmiAvProductsResponse response;
+ if (error_code.has_value()) {
+ response.query_error = error_code.value();
+ return response;
+ }
+
+ // Iterate over the results of the WMI query. Each result will be an
+ // AntiVirusProduct instance.
+ HRESULT hr;
+ while (true) {
+ ComPtr<IWbemClassObject> class_object;
+ ULONG items_returned = 0U;
+ hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
+
+ if (hr == WBEM_S_FALSE || items_returned == 0) {
+ // Reached the end of the enumerator.
+ break;
+ }
+
+ // Something went wrong and it wasn't the end of the enumerator.
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(
+ WmiParsingError::kFailedToIterateResults);
+ continue;
+ }
+
+ base::win::ScopedVariant product_state;
+ hr = class_object->Get(L"productState", 0, product_state.Receive(), 0, 0);
+
+ if (FAILED(hr) || product_state.type() != VT_I4) {
+ response.parsing_errors.push_back(WmiParsingError::kFailedToGetState);
+ continue;
+ }
+
+ LONG state_val = V_I4(product_state.ptr());
+ internal::PRODUCT_STATE product_state_struct;
+ std::copy(reinterpret_cast<const char*>(&state_val),
+ reinterpret_cast<const char*>(&state_val) + sizeof state_val,
+ reinterpret_cast<char*>(&product_state_struct));
+ // Map the values from product_state_struct to the av struct values.
+ AvProduct av_product;
+ switch (product_state_struct.security_state) {
+ case 0:
+ av_product.state = AvProductState::kOff;
+ break;
+ case 1:
+ av_product.state = AvProductState::kOn;
+ break;
+ case 2:
+ av_product.state = AvProductState::kSnoozed;
+ break;
+ default:
+ // Unknown state.
+ response.parsing_errors.push_back(WmiParsingError::kStateInvalid);
+ continue;
+ }
+
+ absl::optional<std::string> display_name =
+ ParseString(L"displayName", class_object);
+ if (!display_name.has_value()) {
+ response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
+ continue;
+ }
+
+ av_product.display_name = display_name.value();
+
+ absl::optional<std::string> product_id =
+ ParseString(L"instanceGuid", class_object);
+ if (!product_id.has_value()) {
+ response.parsing_errors.push_back(WmiParsingError::kFailedToGetId);
+ continue;
+ }
+
+ av_product.product_id = product_id.value();
+
+ // If all values were parsed properly, add `av_product` into the response
+ // vector. If any value could not be parsed properly, the item was
+ // discarded.
+ response.av_products.push_back(av_product);
+ }
+
+ return response;
+}
+
+WmiHotfixesResponse WmiClientImpl::GetInstalledHotfixes() {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+ ComPtr<IEnumWbemClassObject> enumerator;
+ auto error_code = run_query_callback_.Run(
+ base::win::kCimV2ServerName, L"SELECT * FROM Win32_QuickFixEngineering",
+ &enumerator);
+
+ WmiHotfixesResponse response;
+ if (error_code.has_value()) {
+ response.query_error = error_code.value();
+ return response;
+ }
+
+ HRESULT hr;
+ while (true) {
+ ComPtr<IWbemClassObject> class_object;
+ ULONG items_returned = 0U;
+ hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
+
+ if (hr == WBEM_S_FALSE || items_returned == 0U) {
+ // Reached the end of the enumerator.
+ break;
+ }
+
+ // Something went wrong, and it wasn't the end of the enumerator.
+ if (FAILED(hr)) {
+ response.parsing_errors.push_back(
+ WmiParsingError::kFailedToIterateResults);
+ continue;
+ }
+
+ absl::optional<std::string> hotfix_id =
+ ParseString(L"HotFixId", class_object);
+ if (!hotfix_id.has_value()) {
+ response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
+ continue;
+ }
+
+ InstalledHotfix hotfix;
+ hotfix.hotfix_id = hotfix_id.value();
+
+ // If all values were parsed properly, add `hotfix` into the response
+ // vector. If any value could not be parsed properly, the item was
+ // discarded.
+ response.hotfixes.push_back(hotfix);
+ }
+
+ return response;
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.h b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.h
new file mode 100644
index 00000000000..49cb8f8a9a3
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl.h
@@ -0,0 +1,64 @@
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_IMPL_H_
+
+#include "base/callback.h"
+#include "components/device_signals/core/system_signals/win/wmi_client.h"
+
+// WMI interfaces are available on Windows Vista and above, and are officially
+// undocumented.
+namespace device_signals {
+
+class WmiClientImpl : public WmiClient {
+ public:
+ using RunWmiQueryCallback =
+ base::RepeatingCallback<absl::optional<base::win::WmiError>(
+ const std::wstring&,
+ const std::wstring&,
+ Microsoft::WRL::ComPtr<IEnumWbemClassObject>*)>;
+
+ WmiClientImpl();
+
+ ~WmiClientImpl() override;
+
+ // WmiClient:
+ WmiAvProductsResponse GetAntiVirusProducts() override;
+ WmiHotfixesResponse GetInstalledHotfixes() override;
+
+ private:
+ friend class WmiClientImplTest;
+
+ // Constructor taking in a `run_query_callback` which can be used to mock
+ // running the WMI query.
+ explicit WmiClientImpl(RunWmiQueryCallback run_query_callback);
+
+ RunWmiQueryCallback run_query_callback_;
+};
+
+// Type shared in an internal namespace to allow for reuse in unit tests without
+// duplication.
+namespace internal {
+// This is an undocumented structure returned from querying the "productState"
+// uint32 from the AntiVirusProduct in WMI.
+// http://neophob.com/2010/03/wmi-query-windows-securitycenter2/ gives a good
+// summary and testing was also done with a variety of AV products to determine
+// these values as accurately as possible.
+#pragma pack(push)
+#pragma pack(1)
+struct PRODUCT_STATE {
+ uint8_t unknown_1 : 4;
+ uint8_t definition_state : 4; // 1 = Out of date, 0 = Up to date.
+ uint8_t unknown_2 : 4;
+ uint8_t security_state : 4; // 0 = Inactive, 1 = Active, 2 = Snoozed.
+ uint8_t security_provider; // matches WSC_SECURITY_PROVIDER in wscapi.h.
+ uint8_t unknown_3;
+};
+#pragma pack(pop)
+} // namespace internal
+
+} // namespace device_signals
+
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WMI_CLIENT_IMPL_H_
diff --git a/chromium/components/device_signals/core/system_signals/win/wmi_client_impl_unittest.cc b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl_unittest.cc
new file mode 100644
index 00000000000..5331a49211b
--- /dev/null
+++ b/chromium/components/device_signals/core/system_signals/win/wmi_client_impl_unittest.cc
@@ -0,0 +1,263 @@
+// 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 "components/device_signals/core/system_signals/win/wmi_client_impl.h"
+
+#include <wbemidl.h>
+#include <windows.h>
+#include <wrl/client.h>
+#include <wrl/implements.h>
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/test/task_environment.h"
+#include "base/win/wmi.h"
+#include "components/device_signals/core/common/win/win_types.h"
+#include "components/device_signals/core/system_signals/win/com_fakes.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace device_signals {
+
+namespace {
+
+constexpr uint8_t kOffAvState = 0;
+constexpr uint8_t kOnAvState = 1;
+constexpr uint8_t kSnoozedAvState = 2;
+constexpr uint8_t kUnknownAvState = 3;
+
+FakeWbemClassObject CreateHotfixObject(const std::wstring& hotfix_id) {
+ FakeWbemClassObject hotfix_obj;
+ hotfix_obj.Set(L"HotFixId", hotfix_id);
+ return hotfix_obj;
+}
+
+FakeWbemClassObject CreateAvObject(const std::wstring& display_name,
+ const std::wstring& product_id,
+ uint8_t state) {
+ FakeWbemClassObject av_obj;
+ av_obj.Set(L"displayName", display_name);
+ av_obj.Set(L"instanceGuid", product_id);
+
+ internal::PRODUCT_STATE product_state;
+ product_state.security_state = state;
+
+ LONG state_val;
+ std::copy(
+ reinterpret_cast<const char*>(&product_state),
+ reinterpret_cast<const char*>(&product_state) + sizeof product_state,
+ reinterpret_cast<char*>(&state_val));
+ av_obj.Set(L"productState", state_val);
+
+ return av_obj;
+}
+
+} // namespace
+
+class WmiClientImplTest : public testing::Test {
+ public:
+ WmiClientImplTest()
+ : wmi_client_(base::BindRepeating(&WmiClientImplTest::RunQuery,
+ base::Unretained(this))) {}
+
+ protected:
+ absl::optional<base::win::WmiError> RunQuery(
+ const std::wstring& server_name,
+ const std::wstring& query,
+ ComPtr<IEnumWbemClassObject>* enumerator) {
+ ++nb_calls_;
+ captured_server_name_ = server_name;
+ captured_query_ = query;
+
+ if (query_error_.has_value()) {
+ return query_error_.value();
+ }
+ *enumerator = &fake_enumerator_;
+ return absl::nullopt;
+ }
+
+ void ExpectAvQueryRan() {
+ EXPECT_EQ(captured_server_name_, base::win::kSecurityCenter2ServerName);
+ EXPECT_EQ(captured_query_, L"SELECT * FROM AntiVirusProduct");
+ EXPECT_EQ(nb_calls_, 1U);
+ }
+
+ void ExpectHotfixQueryRan() {
+ EXPECT_EQ(captured_server_name_, base::win::kCimV2ServerName);
+ EXPECT_EQ(captured_query_, L"SELECT * FROM Win32_QuickFixEngineering");
+ EXPECT_EQ(nb_calls_, 1U);
+ }
+
+ FakeEnumWbemClassObject fake_enumerator_;
+ absl::optional<base::win::WmiError> query_error_;
+
+ std::wstring captured_server_name_;
+ std::wstring captured_query_;
+ uint32_t nb_calls_ = 0;
+
+ WmiClientImpl wmi_client_;
+};
+
+// Tests how the client behaves when the WMI query fails when querying for AV
+// products.
+TEST_F(WmiClientImplTest, GetAntiVirusProducts_FailedRunQuery) {
+ query_error_ = base::win::WmiError::kFailedToConnectToWMI;
+
+ auto av_response = wmi_client_.GetAntiVirusProducts();
+
+ ExpectAvQueryRan();
+ EXPECT_EQ(av_response.av_products.size(), 0U);
+ EXPECT_EQ(av_response.parsing_errors.size(), 0U);
+ EXPECT_EQ(av_response.query_error,
+ base::win::WmiError::kFailedToConnectToWMI);
+}
+
+// Tests how the client behaves when iterating through objects with all known
+// AV product states.
+TEST_F(WmiClientImplTest, GetAntiVirusProducts_AllProductStates) {
+ std::wstring display_name1 = L"Av Display Name 1";
+ std::wstring product_id1 = L"product ID 1";
+ FakeWbemClassObject av_obj1 =
+ CreateAvObject(display_name1, product_id1, kOffAvState);
+
+ std::wstring display_name2 = L"Av Display Name 2";
+ std::wstring product_id2 = L"product ID 2";
+ FakeWbemClassObject av_obj2 =
+ CreateAvObject(display_name2, product_id2, kOnAvState);
+
+ std::wstring display_name3 = L"Av Display Name 3";
+ std::wstring product_id3 = L"product ID 3";
+ FakeWbemClassObject av_obj3 =
+ CreateAvObject(display_name3, product_id3, kSnoozedAvState);
+
+ std::wstring display_name4 = L"Av Display Name 4";
+ std::wstring product_id4 = L"product ID 4";
+ FakeWbemClassObject av_obj4 =
+ CreateAvObject(display_name3, product_id4, kUnknownAvState);
+
+ fake_enumerator_.Add(&av_obj1);
+ fake_enumerator_.Add(&av_obj2);
+ fake_enumerator_.Add(&av_obj3);
+ fake_enumerator_.Add(&av_obj4);
+
+ auto av_response = wmi_client_.GetAntiVirusProducts();
+
+ ExpectAvQueryRan();
+ EXPECT_EQ(av_response.query_error, absl::nullopt);
+
+ // Known states were parsed successfully.
+ EXPECT_EQ(av_response.av_products.size(), 3U);
+ EXPECT_EQ(av_response.av_products[0].display_name,
+ base::SysWideToUTF8(display_name1));
+ EXPECT_EQ(av_response.av_products[0].product_id,
+ base::SysWideToUTF8(product_id1));
+ EXPECT_EQ(av_response.av_products[0].state, AvProductState::kOff);
+ EXPECT_EQ(av_response.av_products[1].display_name,
+ base::SysWideToUTF8(display_name2));
+ EXPECT_EQ(av_response.av_products[1].product_id,
+ base::SysWideToUTF8(product_id2));
+ EXPECT_EQ(av_response.av_products[1].state, AvProductState::kOn);
+ EXPECT_EQ(av_response.av_products[2].display_name,
+ base::SysWideToUTF8(display_name3));
+ EXPECT_EQ(av_response.av_products[2].product_id,
+ base::SysWideToUTF8(product_id3));
+ EXPECT_EQ(av_response.av_products[2].state, AvProductState::kSnoozed);
+
+ // Unknown state is returned as parsing error.
+ ASSERT_EQ(av_response.parsing_errors.size(), 1U);
+ EXPECT_EQ(av_response.parsing_errors[0], WmiParsingError::kStateInvalid);
+}
+
+// Tests how the client behaves when parsing an AV object which is missing the
+// displayName value.
+TEST_F(WmiClientImplTest, GetAntiVirusProducts_MissingDisplayName) {
+ FakeWbemClassObject av_obj1 =
+ CreateAvObject(L"Av Display Name", L"product ID", kOffAvState);
+
+ av_obj1.Delete(L"displayName");
+
+ fake_enumerator_.Add(&av_obj1);
+
+ auto av_response = wmi_client_.GetAntiVirusProducts();
+
+ ExpectAvQueryRan();
+ EXPECT_EQ(av_response.query_error, absl::nullopt);
+ EXPECT_EQ(av_response.av_products.size(), 0U);
+
+ // Missing name property is returned as parsing error.
+ ASSERT_EQ(av_response.parsing_errors.size(), 1U);
+ EXPECT_EQ(av_response.parsing_errors[0], WmiParsingError::kFailedToGetName);
+}
+
+// Tests how the client behaves when parsing an AV object which is missing the
+// displayName value.
+TEST_F(WmiClientImplTest, GetAntiVirusProducts_MissingProductId) {
+ FakeWbemClassObject av_obj1 =
+ CreateAvObject(L"Av Display Name", L"product ID", kOffAvState);
+
+ av_obj1.Delete(L"instanceGuid");
+
+ fake_enumerator_.Add(&av_obj1);
+
+ auto av_response = wmi_client_.GetAntiVirusProducts();
+
+ ExpectAvQueryRan();
+ EXPECT_EQ(av_response.query_error, absl::nullopt);
+ EXPECT_EQ(av_response.av_products.size(), 0U);
+
+ // Missing ID property is returned as parsing error.
+ ASSERT_EQ(av_response.parsing_errors.size(), 1U);
+ EXPECT_EQ(av_response.parsing_errors[0], WmiParsingError::kFailedToGetId);
+}
+
+// Tests how the client behaves when the WMI query fails when querying for
+// installed hotfixes.
+TEST_F(WmiClientImplTest, GetInstalledHotfixes_FailedRunQuery) {
+ query_error_ = base::win::WmiError::kFailedToConnectToWMI;
+
+ auto hotfix_response = wmi_client_.GetInstalledHotfixes();
+
+ ExpectHotfixQueryRan();
+ EXPECT_EQ(hotfix_response.hotfixes.size(), 0U);
+ EXPECT_EQ(hotfix_response.parsing_errors.size(), 0U);
+ EXPECT_EQ(hotfix_response.query_error,
+ base::win::WmiError::kFailedToConnectToWMI);
+}
+
+// Tests how the client behaves when parsing hotfix objects, one of which is
+// valid, and another which is missing an ID.
+TEST_F(WmiClientImplTest, GetInstalledHotfixes_ParsingItems) {
+ std::wstring hotfix_id1 = L"some_hotfix_id";
+ FakeWbemClassObject hotfix_obj1 = CreateHotfixObject(hotfix_id1);
+
+ FakeWbemClassObject hotfix_obj2 = CreateHotfixObject(L"some_other_id");
+
+ hotfix_obj2.Delete(L"HotFixId");
+
+ fake_enumerator_.Add(&hotfix_obj1);
+ fake_enumerator_.Add(&hotfix_obj2);
+
+ auto hotfix_response = wmi_client_.GetInstalledHotfixes();
+
+ ExpectHotfixQueryRan();
+ EXPECT_EQ(hotfix_response.query_error, absl::nullopt);
+
+ // Success item.
+ ASSERT_EQ(hotfix_response.hotfixes.size(), 1U);
+ EXPECT_EQ(hotfix_response.hotfixes[0].hotfix_id,
+ base::SysWideToUTF8(hotfix_id1));
+
+ // Failed item.
+ ASSERT_EQ(hotfix_response.parsing_errors.size(), 1U);
+ EXPECT_EQ(hotfix_response.parsing_errors[0],
+ WmiParsingError::kFailedToGetName);
+}
+
+} // namespace device_signals
diff --git a/chromium/components/device_signals/core/common/win/wsc_client.cc b/chromium/components/device_signals/core/system_signals/win/wsc_client.cc
index 4b44c206ef8..7a8d80c4aee 100644
--- a/chromium/components/device_signals/core/common/win/wsc_client.cc
+++ b/chromium/components/device_signals/core/system_signals/win/wsc_client.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/wsc_client.h"
+#include "components/device_signals/core/system_signals/win/wsc_client.h"
namespace device_signals {
diff --git a/chromium/components/device_signals/core/common/win/wsc_client.h b/chromium/components/device_signals/core/system_signals/win/wsc_client.h
index 089808eee8d..f90390a72c5 100644
--- a/chromium/components/device_signals/core/common/win/wsc_client.h
+++ b/chromium/components/device_signals/core/system_signals/win/wsc_client.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_H_
#include <string>
#include <vector>
@@ -15,13 +15,19 @@
// https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/
namespace device_signals {
+// Errors that can occur when querying WSC.
+// Do not change ordering. This enum is captured as
+// `DeviceSignalsWscQueryError` in enums.xml.
enum class WscQueryError {
kFailedToCreateInstance = 0,
kFailedToInitializeProductList = 1,
kFailedToGetProductCount = 2,
+ kMaxValue = kFailedToGetProductCount
};
// Errors that can occur when calling WSC, or parsing response values.
+// Do not change ordering. This enum is captured as
+// `DeviceSignalsWscParsingError` in enums.xml.
enum class WscParsingError {
kFailedToGetItem = 0,
kFailedToGetState = 1,
@@ -56,4 +62,4 @@ class WscClient {
} // namespace device_signals
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_H_
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_H_
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl.cc b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl.cc
index fae2aed1514..3dfd5ecb04c 100644
--- a/chromium/components/device_signals/core/common/win/wsc_client_impl.cc
+++ b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/wsc_client_impl.h"
+#include "components/device_signals/core/system_signals/win/wsc_client_impl.h"
#include <iwscapi.h>
#include <windows.h>
@@ -40,9 +40,17 @@ AvProductState ConvertState(WSC_SECURITY_PRODUCT_STATE state) {
}
}
-HRESULT CreateProductList(IWSCProductList** product_list) {
- return ::CoCreateInstance(__uuidof(WSCProductList), nullptr,
- CLSCTX_INPROC_SERVER, IID_PPV_ARGS(product_list));
+HRESULT CreateProductList(ComPtr<IWSCProductList>* out_product_list) {
+ ComPtr<IWSCProductList> product_list;
+ HRESULT hr =
+ ::CoCreateInstance(__uuidof(WSCProductList), nullptr,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&product_list));
+
+ if (!FAILED(hr)) {
+ *out_product_list = product_list;
+ }
+
+ return hr;
}
} // namespace
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl.h b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl.h
index e17df9d36ef..3d32ae791c6 100644
--- a/chromium/components/device_signals/core/common/win/wsc_client_impl.h
+++ b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl.h
@@ -1,21 +1,21 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
-#define COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
+#ifndef COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_IMPL_H_
+#define COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_IMPL_H_
#include <iwscapi.h>
#include "base/callback.h"
-#include "components/device_signals/core/common/win/wsc_client.h"
+#include "components/device_signals/core/system_signals/win/wsc_client.h"
namespace device_signals {
class WscClientImpl : public WscClient {
public:
- using CreateProductListCallback =
- base::RepeatingCallback<HRESULT(IWSCProductList**)>;
+ using CreateProductListCallback = base::RepeatingCallback<HRESULT(
+ Microsoft::WRL::ComPtr<IWSCProductList>*)>;
WscClientImpl();
@@ -36,4 +36,4 @@ class WscClientImpl : public WscClient {
} // namespace device_signals
-#endif // COMPONENTS_DEVICE_SIGNALS_CORE_COMMON_WIN_WSC_CLIENT_IMPL_H_
+#endif // COMPONENTS_DEVICE_SIGNALS_CORE_SYSTEM_SIGNALS_WIN_WSC_CLIENT_IMPL_H_
diff --git a/chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl_unittest.cc
index e922328ad36..ae56cf38985 100644
--- a/chromium/components/device_signals/core/common/win/wsc_client_impl_unittest.cc
+++ b/chromium/components/device_signals/core/system_signals/win/wsc_client_impl_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// 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 "components/device_signals/core/common/win/wsc_client_impl.h"
+#include "components/device_signals/core/system_signals/win/wsc_client_impl.h"
#include <iwscapi.h>
#include <windows.h>
@@ -15,9 +15,10 @@
#include "base/callback.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/task_environment.h"
+#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
-#include "components/device_signals/core/common/win/com_fakes.h"
#include "components/device_signals/core/common/win/win_types.h"
+#include "components/device_signals/core/system_signals/win/com_fakes.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -39,7 +40,7 @@ class WscClientImplTest : public testing::Test {
: wsc_client_(base::BindRepeating(&WscClientImplTest::CreateProductList,
base::Unretained(this))) {}
- HRESULT CreateProductList(IWSCProductList** product_list) {
+ HRESULT CreateProductList(ComPtr<IWSCProductList>* product_list) {
if (fail_list_creation_) {
return E_FAIL;
}
@@ -223,6 +224,8 @@ TEST_F(WscClientImplTest, GetAntiVirusProducts_ProductErrors) {
// Smoke/sanity test to verify that Defender's instance GUID does not change
// over time. This test actually calls WSC.
TEST(RealWscClientImplTest, SmokeWsc_GetAntiVirusProducts) {
+ base::win::ScopedCOMInitializer scoped_com_initializer;
+
// That part of the display name is not translated when getting it from WSC,
// so it can be used quite simply.
constexpr char kPartialDefenderName[] = "Microsoft Defender";