Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(280)

Side by Side Diff: chrome/browser/metrics/antivirus_metrics_provider_win.cc

Issue 2009773007: Add AntiVirus information to the system profile. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: code review fixes Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/metrics/antivirus_metrics_provider_win.h"
6
7 #include <iwscapi.h>
8 #include <stddef.h>
9 #include <windows.h>
10 #include <wscapi.h>
11
12 #include <string>
13
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/feature_list.h"
17 #include "base/file_version_info_win.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/metrics/field_trial.h"
21 #include "base/path_service.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/sys_string_conversions.h"
24 #include "base/task_runner_util.h"
25 #include "base/threading/thread_restrictions.h"
26 #include "base/version.h"
27 #include "base/win/scoped_bstr.h"
28 #include "base/win/scoped_com_initializer.h"
29 #include "base/win/scoped_comptr.h"
30 #include "base/win/windows_version.h"
31 #include "chrome/common/channel_info.h"
32 #include "components/metrics/proto/system_profile.pb.h"
33 #include "components/variations/metrics_util.h"
34 #include "components/version_info/version_info.h"
35
36 namespace {
37
38 const base::Feature kReportFullAvProductDetails {
39 AntiVirusMetricsProvider::kReportFullAvProductDetailsName,
40 base::FEATURE_DISABLED_BY_DEFAULT
41 };
42
43 bool ShouldReportFullNames() {
44 // The expectation is that this will be disabled for the majority of users,
45 // but this allows a small group to be enabled on other channels if there are
46 // a large percentage of hashes collected on these channels that are not
47 // resolved to names previously collected on Canary channel.
48 bool enabled = base::FeatureList::IsEnabled(kReportFullAvProductDetails);
49
50 if (chrome::GetChannel() == version_info::Channel::CANARY)
51 return true;
52
53 return enabled;
54 }
55
56 // Helper function for expanding all environment variables in |path|.
57 std::wstring ExpandEnvironmentVariables(const std::wstring& path) {
58 static const DWORD kMaxBuffer = 32 * 1024; // Max according to MSDN.
59 std::wstring path_expanded;
60 DWORD path_len = MAX_PATH;
61 do {
62 DWORD result = ExpandEnvironmentStrings(
63 path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
64 if (!result) {
65 // Failed to expand variables. Return the original string.
66 DPLOG(ERROR) << path;
67 break;
68 }
69 if (result <= path_len)
70 return path_expanded.substr(0, result - 1);
71 path_len = result;
72 } while (path_len < kMaxBuffer);
73
74 return path;
75 }
76
77 } // namespace
78
79 AntiVirusMetricsProvider::AntiVirusMetricsProvider(
80 scoped_refptr<base::SequencedTaskRunner> file_thread)
81 : file_thread_(file_thread), weak_ptr_factory_(this) {}
82
83 AntiVirusMetricsProvider::~AntiVirusMetricsProvider() {}
84
85 void AntiVirusMetricsProvider::ProvideSystemProfileMetrics(
86 metrics::SystemProfileProto* system_profile_proto) {
87 for (const auto& av_product : av_products_) {
88 auto product = system_profile_proto->add_antivirus_product();
89
90 std::string product_name = base::SysWideToUTF8(av_product.product_name);
91
92 if (ShouldReportFullNames())
93 product->set_product_name(product_name);
94 product->set_product_name_hash(metrics::HashName(product_name));
95
96 if (!av_product.product_version.empty()) {
97 std::string product_version =
98 base::SysWideToUTF8(av_product.product_version);
99 if (ShouldReportFullNames())
100 product->set_product_version(product_version);
101 product->set_product_version_hash(metrics::HashName(product_version));
102 }
103
104 product->set_product_state(av_product.product_state);
105 }
106 }
107
108 void AntiVirusMetricsProvider::GetAntiVirusMetrics(
109 const base::Closure& done_callback) {
110 base::PostTaskAndReplyWithResult(
111 file_thread_.get(), FROM_HERE,
112 base::Bind(&AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread),
113 base::Bind(&AntiVirusMetricsProvider::GotAntiVirusProducts,
114 weak_ptr_factory_.GetWeakPtr(), done_callback));
115 }
116
117 // static
118 AntiVirusMetricsProvider::AvProductList
119 AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread() {
120 AvProductList av_products;
121
122 FillAntiVirusProducts(&av_products);
Alexei Svitkine (slow) 2016/06/02 21:46:57 Do you want to report that AV queries failed someh
Will Harris 2016/06/02 23:57:17 added a histogram to report failures.
123
124 return av_products;
125 }
126
127 void AntiVirusMetricsProvider::GotAntiVirusProducts(
128 const base::Closure& done_callback,
129 const AvProductList& av_products) {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 av_products_ = av_products;
132 done_callback.Run();
133 }
134
135 // static
136 bool AntiVirusMetricsProvider::FillAntiVirusProducts(AvProductList* products) {
137 base::ThreadRestrictions::AssertIOAllowed();
138 base::win::ScopedCOMInitializer com_initializer;
139
140 if (!com_initializer.succeeded())
141 return false;
142
143 base::win::ScopedComPtr<IWSCProductList> product_list;
144 HRESULT result =
145 CoCreateInstance(__uuidof(WSCProductList), NULL, CLSCTX_INPROC_SERVER,
146 __uuidof(IWSCProductList), product_list.ReceiveVoid());
147 if (FAILED(result))
148 return false;
149
150 result = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS);
151 if (FAILED(result))
152 return false;
153
154 LONG product_count;
155 result = product_list->get_Count(&product_count);
156 if (FAILED(result))
157 return false;
158
159 for (LONG i = 0; i < product_count; i++) {
160 IWscProduct* product = nullptr;
161 result = product_list->get_Item(i, &product);
162 if (FAILED(result))
163 return false;
164
165 static_assert(metrics::SystemProfileProto::AntiVirusState::
166 SystemProfileProto_AntiVirusState_STATE_ON ==
167 static_cast<metrics::SystemProfileProto::AntiVirusState>(
168 WSC_SECURITY_PRODUCT_STATE_ON),
169 "proto and API values must be the same");
170 static_assert(metrics::SystemProfileProto::AntiVirusState::
171 SystemProfileProto_AntiVirusState_STATE_OFF ==
172 static_cast<metrics::SystemProfileProto::AntiVirusState>(
173 WSC_SECURITY_PRODUCT_STATE_OFF),
174 "proto and API values must be the same");
175 static_assert(metrics::SystemProfileProto::AntiVirusState::
176 SystemProfileProto_AntiVirusState_STATE_SNOOZED ==
177 static_cast<metrics::SystemProfileProto::AntiVirusState>(
178 WSC_SECURITY_PRODUCT_STATE_SNOOZED),
179 "proto and API values must be the same");
180 static_assert(metrics::SystemProfileProto::AntiVirusState::
181 SystemProfileProto_AntiVirusState_STATE_EXPIRED ==
182 static_cast<metrics::SystemProfileProto::AntiVirusState>(
183 WSC_SECURITY_PRODUCT_STATE_EXPIRED),
184 "proto and API values must be the same");
185
186 AvProduct av_product;
187 WSC_SECURITY_PRODUCT_STATE product_state;
188 result = product->get_ProductState(&product_state);
189 if (FAILED(result))
Alexei Svitkine (slow) 2016/06/02 21:46:57 Should we skip individual entries where we have a
Will Harris 2016/06/02 23:57:17 I'm making this fail, and if we get lots of errors
190 return false;
191
192 av_product.product_state =
193 static_cast<metrics::SystemProfileProto::AntiVirusState>(product_state);
194
195 if (av_product.product_state <
196 metrics::SystemProfileProto::AntiVirusState_MIN ||
197 av_product.product_state >
198 metrics::SystemProfileProto::AntiVirusState_MAX) {
Alexei Svitkine (slow) 2016/06/02 21:46:57 Set to OTHER here instead?
Will Harris 2016/06/02 23:57:17 should be covered by RESULT_PRODUCT_STATE_INVALID.
Alexei Svitkine (slow) 2016/06/03 15:48:33 I still don't buy this - but maybe I'm missing som
Will Harris 2016/06/03 16:01:21 the latest code uses metrics::SystemProfileProto_A
Alexei Svitkine (slow) 2016/06/03 16:14:15 I agree it's cleaner to use that to check, however
Will Harris 2016/06/03 16:28:56 Either way achieves the same end goal. with the OT
Alexei Svitkine (slow) 2016/06/03 16:57:03 OK. It does mean that we'll want to have this hist
199 return false;
200 }
201
202 base::win::ScopedBstr product_name;
203 result = product->get_ProductName(product_name.Receive());
204 if (FAILED(result))
205 return false;
206 av_product.product_name = std::wstring(product_name, product_name.Length());
207 product_name.Release();
208
209 base::win::ScopedBstr remediation_path;
210 result = product->get_RemediationPath(remediation_path.Receive());
211 if (FAILED(result))
212 return false;
213 std::wstring path_str(remediation_path, remediation_path.Length());
214 remediation_path.Release();
215
216 base::FilePath full_path(ExpandEnvironmentVariables(path_str));
217
218 #if !defined(_WIN64)
219 if (!base::PathExists(full_path)) {
220 // On 32-bit builds, path might contain C:\Program Files (x86) instead of
221 // C:\Program Files.
222 base::ReplaceFirstSubstringAfterOffset(&path_str, 0, L"%ProgramFiles%",
223 L"%ProgramW6432%");
224 full_path = base::FilePath(ExpandEnvironmentVariables(path_str));
225 }
226 #endif // !defined(_WIN64)
227 std::unique_ptr<FileVersionInfo> version_info(
228 FileVersionInfo::CreateFileVersionInfo(full_path));
229
230 if (version_info.get()) {
231 FileVersionInfoWin* version_info_win =
232 static_cast<FileVersionInfoWin*>(version_info.get());
233 av_product.product_version = version_info_win->product_version();
Alexei Svitkine (slow) 2016/06/02 21:46:57 Can you make GetProductionVersion() a helper funct
Will Harris 2016/06/02 23:57:17 Done.
234 } else {
235 av_product.product_version = std::wstring();
236 }
237
238 products->push_back(av_product);
239 }
240
241 return true;
242 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698