Chromium Code Reviews| Index: chrome/browser/metrics/antivirus_metrics_provider_win.cc |
| diff --git a/chrome/browser/metrics/antivirus_metrics_provider_win.cc b/chrome/browser/metrics/antivirus_metrics_provider_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7d6b22fd579f5851bf37f7624f2290708368f39b |
| --- /dev/null |
| +++ b/chrome/browser/metrics/antivirus_metrics_provider_win.cc |
| @@ -0,0 +1,238 @@ |
| +// Copyright 2016 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 "chrome/browser/metrics/antivirus_metrics_provider_win.h" |
| + |
| +#include <iwscapi.h> |
| +#include <stddef.h> |
| +#include <windows.h> |
| +#include <wscapi.h> |
| + |
| +#include <string> |
| + |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/file_version_info_win.h" |
| +#include "base/files/file_path.h" |
| +#include "base/files/file_util.h" |
| +#include "base/metrics/field_trial.h" |
| +#include "base/path_service.h" |
| +#include "base/strings/string_util.h" |
| +#include "base/strings/sys_string_conversions.h" |
| +#include "base/task_runner_util.h" |
| +#include "base/threading/thread_restrictions.h" |
| +#include "base/version.h" |
| +#include "base/win/scoped_bstr.h" |
| +#include "base/win/scoped_com_initializer.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "base/win/windows_version.h" |
| +#include "chrome/common/channel_info.h" |
| +#include "components/metrics/proto/system_profile.pb.h" |
| +#include "components/variations/metrics_util.h" |
| +#include "components/version_info/version_info.h" |
| + |
| +namespace metrics { |
|
Alexei Svitkine (slow)
2016/06/01 20:42:34
Only things in components/metrics should be in the
Will Harris
2016/06/02 20:36:26
Happy to move out of namespace but there seems to
Alexei Svitkine (slow)
2016/06/02 21:46:57
Sigh. I didn't realize this many things were incon
Will Harris
2016/06/02 23:57:17
I'll leave this new code outside metrics namespace
|
| + |
| +namespace { |
| + |
| +bool ShouldReportFullNames() { |
| + const std::string group_name = |
| + base::FieldTrialList::FindFullName("ReportFullAVProductDetails"); |
| + |
| + if (chrome::GetChannel() == version_info::Channel::CANARY || |
| + chrome::GetChannel() == version_info::Channel::UNKNOWN) { |
|
Alexei Svitkine (slow)
2016/06/01 20:42:35
I'd rather we don't default unknown to true. Canar
Will Harris
2016/06/01 23:57:08
I think the tests/bots run as unknown. if I make i
Alexei Svitkine (slow)
2016/06/02 15:17:18
Then I suggest your tests to explicitly enable the
Will Harris
2016/06/02 20:36:27
Done.
|
| + return true; |
| + } |
| + |
| + return base::StartsWith(group_name, "Enabled", |
| + base::CompareCase::INSENSITIVE_ASCII); |
|
Alexei Svitkine (slow)
2016/06/01 20:42:35
Use base::Feature API instead - which is now the b
Will Harris
2016/06/02 20:36:27
Done.
|
| +} |
| + |
| +// Helper function for expanding all environment variables in |path|. |
| +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. Return the original string. |
| + DPLOG(ERROR) << path; |
| + break; |
| + } |
| + if (result <= path_len) |
| + return path_expanded.substr(0, result - 1); |
| + path_len = result; |
| + } while (path_len < kMaxBuffer); |
| + |
| + return path; |
| +} |
| + |
| +} // namespace |
| + |
| +AntiVirusMetricsProvider::AntiVirusMetricsProvider( |
| + scoped_refptr<base::SequencedTaskRunner> file_thread) |
| + : file_thread_(file_thread), weak_ptr_factory_(this) {} |
| + |
| +AntiVirusMetricsProvider::~AntiVirusMetricsProvider() {} |
| + |
| +void AntiVirusMetricsProvider::ProvideSystemProfileMetrics( |
| + SystemProfileProto* system_profile_proto) { |
| + for (const auto& av_product : av_products_) { |
| + auto product = system_profile_proto->add_antivirus_product(); |
| + |
| + std::string product_name = base::SysWideToUTF8(av_product.product_name); |
| + |
| + if (ShouldReportFullNames()) |
| + product->set_product_name(product_name); |
| + product->set_product_name_hash(HashName(product_name)); |
|
Alexei Svitkine (slow)
2016/06/01 20:42:35
How about doing all this logic once (as opposed to
Will Harris
2016/06/02 20:36:27
I wanted to have the FillAntiVirusProducts be call
Alexei Svitkine (slow)
2016/06/02 21:46:57
I think it's inefficient to do this logic over and
|
| + |
| + if (!av_product.product_version.empty()) { |
| + std::string product_version = |
| + base::SysWideToUTF8(av_product.product_version); |
| + if (ShouldReportFullNames()) |
| + product->set_product_version(product_version); |
| + product->set_product_version_hash(HashName(product_version)); |
| + } |
| + |
| + product->set_product_state(av_product.product_state); |
| + } |
| +} |
| + |
| +void AntiVirusMetricsProvider::GetAntiVirusMetrics( |
| + const base::Closure& done_callback) { |
| + base::PostTaskAndReplyWithResult( |
| + file_thread_.get(), FROM_HERE, |
| + base::Bind(&AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread), |
| + base::Bind(&AntiVirusMetricsProvider::GotAntiVirusProducts, |
| + weak_ptr_factory_.GetWeakPtr(), done_callback)); |
| +} |
| + |
| +// static |
| +AntiVirusMetricsProvider::AvProductList |
| +AntiVirusMetricsProvider::GetAntiVirusProductsOnFileThread() { |
| + AvProductList av_products; |
| + |
| + FillAntiVirusProducts(&av_products); |
| + |
| + return av_products; |
| +} |
| + |
| +void AntiVirusMetricsProvider::GotAntiVirusProducts( |
| + const base::Closure& done_callback, |
| + const AvProductList& av_products) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + av_products_ = av_products; |
| + done_callback.Run(); |
| +} |
| + |
| +// static |
| +bool AntiVirusMetricsProvider::FillAntiVirusProducts(AvProductList* products) { |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + base::win::ScopedCOMInitializer com_initializer; |
| + |
| + if (!com_initializer.succeeded()) |
| + return false; |
| + |
| + base::win::ScopedComPtr<IWSCProductList> product_list; |
| + HRESULT result = |
| + CoCreateInstance(__uuidof(WSCProductList), NULL, CLSCTX_INPROC_SERVER, |
| + __uuidof(IWSCProductList), product_list.ReceiveVoid()); |
| + if (FAILED(result)) |
| + return false; |
| + |
| + result = product_list->Initialize(WSC_SECURITY_PROVIDER_ANTIVIRUS); |
| + if (FAILED(result)) |
| + return false; |
| + |
| + LONG product_count; |
| + result = product_list->get_Count(&product_count); |
| + if (FAILED(result)) |
| + return false; |
| + |
| + for (LONG i = 0; i < product_count; i++) { |
| + IWscProduct* product = nullptr; |
| + result = product_list->get_Item(i, &product); |
| + if (FAILED(result)) |
| + return false; |
| + |
| + static_assert(SystemProfileProto::AntiVirusState:: |
| + SystemProfileProto_AntiVirusState_STATE_ON == |
| + static_cast<SystemProfileProto::AntiVirusState>( |
| + WSC_SECURITY_PRODUCT_STATE_ON), |
| + "proto and API values must be the same"); |
| + static_assert(SystemProfileProto::AntiVirusState:: |
| + SystemProfileProto_AntiVirusState_STATE_OFF == |
| + static_cast<SystemProfileProto::AntiVirusState>( |
| + WSC_SECURITY_PRODUCT_STATE_OFF), |
| + "proto and API values must be the same"); |
| + static_assert(SystemProfileProto::AntiVirusState:: |
| + SystemProfileProto_AntiVirusState_STATE_SNOOZED == |
| + static_cast<SystemProfileProto::AntiVirusState>( |
| + WSC_SECURITY_PRODUCT_STATE_SNOOZED), |
| + "proto and API values must be the same"); |
| + static_assert(SystemProfileProto::AntiVirusState:: |
| + SystemProfileProto_AntiVirusState_STATE_EXPIRED == |
| + static_cast<SystemProfileProto::AntiVirusState>( |
| + WSC_SECURITY_PRODUCT_STATE_EXPIRED), |
| + "proto and API values must be the same"); |
| + |
| + AvProduct av_product; |
| + WSC_SECURITY_PRODUCT_STATE product_state; |
| + result = product->get_ProductState(&product_state); |
| + if (FAILED(result)) |
| + return false; |
| + |
| + av_product.product_state = |
| + static_cast<SystemProfileProto::AntiVirusState>(product_state); |
| + |
| + if (av_product.product_state < SystemProfileProto::AntiVirusState_MIN || |
| + av_product.product_state > SystemProfileProto::AntiVirusState_MAX) { |
| + return false; |
| + } |
| + |
| + base::win::ScopedBstr product_name; |
| + result = product->get_ProductName(product_name.Receive()); |
| + if (FAILED(result)) |
| + return false; |
| + av_product.product_name = std::wstring(product_name, product_name.Length()); |
| + product_name.Release(); |
| + |
| + base::win::ScopedBstr remediation_path; |
| + result = product->get_RemediationPath(remediation_path.Receive()); |
| + if (FAILED(result)) |
| + return false; |
| + std::wstring path_str(remediation_path, remediation_path.Length()); |
| + remediation_path.Release(); |
| + |
| + base::FilePath full_path(ExpandEnvironmentVariables(path_str)); |
| + |
| +#if !defined(_WIN64) |
| + if (!base::PathExists(full_path)) { |
| + // On 32-bit builds, path might contain C:\Program Files (x86) instead of |
| + // C:\Program Files. |
| + base::ReplaceFirstSubstringAfterOffset(&path_str, 0, L"%ProgramFiles%", |
| + L"%ProgramW6432%"); |
| + full_path = base::FilePath(ExpandEnvironmentVariables(path_str)); |
| + } |
| +#endif // !defined(_WIN64) |
| + std::unique_ptr<FileVersionInfo> version_info( |
| + FileVersionInfo::CreateFileVersionInfo(full_path)); |
| + |
| + if (version_info.get()) { |
| + FileVersionInfoWin* version_info_win = |
| + static_cast<FileVersionInfoWin*>(version_info.get()); |
| + av_product.product_version = version_info_win->product_version(); |
| + } else { |
| + av_product.product_version = std::wstring(); |
| + } |
| + |
| + products->push_back(av_product); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace metrics |