xref: /aosp_15_r20/external/cronet/components/metrics/motherboard.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors
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 "components/metrics/motherboard.h"
6 
7 #include <optional>
8 #include <string>
9 #include <utility>
10 
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_util.h"
14 #include "build/build_config.h"
15 
16 #if BUILDFLAG(IS_WIN)
17 #include <windows.h>
18 
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/scoped_blocking_call.h"
21 #include "base/win/scoped_bstr.h"
22 #include "base/win/scoped_variant.h"
23 #include "base/win/wmi.h"
24 #endif
25 
26 namespace metrics {
27 namespace {
28 
29 struct MotherboardDetails {
30   std::optional<std::string> manufacturer;
31   std::optional<std::string> model;
32   std::optional<std::string> bios_manufacturer;
33   std::optional<std::string> bios_version;
34   std::optional<Motherboard::BiosType> bios_type;
35 };
36 
37 #if BUILDFLAG(IS_LINUX)
38 using base::FilePath;
39 using base::PathExists;
40 using base::ReadFileToString;
41 using base::TrimWhitespaceASCII;
42 using base::TRIM_TRAILING;
43 
ReadMotherboardDetails()44 MotherboardDetails ReadMotherboardDetails() {
45   constexpr FilePath::CharType kDmiPath[] = "/sys/devices/virtual/dmi/id";
46   constexpr FilePath::CharType kEfiPath[] = "/sys/firmware/efi";
47   const FilePath dmi_path(kDmiPath);
48   MotherboardDetails details;
49   std::string temp;
50   if (ReadFileToString(dmi_path.Append("board_vendor"), &temp)) {
51     details.manufacturer =
52         std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
53   }
54   if (ReadFileToString(dmi_path.Append("board_name"), &temp)) {
55     details.model = std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
56   }
57   if (ReadFileToString(dmi_path.Append("bios_vendor"), &temp)) {
58     details.bios_manufacturer =
59         std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
60   }
61   if (ReadFileToString(dmi_path.Append("bios_version"), &temp)) {
62     details.bios_version =
63         std::string(TrimWhitespaceASCII(temp, TRIM_TRAILING));
64   }
65   if (PathExists(FilePath(kEfiPath))) {
66     details.bios_type = Motherboard::BiosType::kUefi;
67   } else {
68     details.bios_type = Motherboard::BiosType::kLegacy;
69   }
70   return details;
71 }
72 #endif
73 
74 #if BUILDFLAG(IS_WIN)
75 using Microsoft::WRL::ComPtr;
76 using base::win::ScopedBstr;
77 using base::win::ScopedVariant;
78 
ReadStringMember(ComPtr<IWbemClassObject> class_object,const wchar_t * key)79 std::optional<std::string> ReadStringMember(
80     ComPtr<IWbemClassObject> class_object,
81     const wchar_t* key) {
82   ScopedVariant variant;
83   HRESULT hr = class_object->Get(key, 0, variant.Receive(), 0, 0);
84   if (SUCCEEDED(hr) && variant.type() == VT_BSTR) {
85     const auto len = ::SysStringLen(V_BSTR(variant.ptr()));
86     std::wstring wstr(V_BSTR(variant.ptr()), len);
87     return base::WideToUTF8(wstr);
88   }
89   return {};
90 }
91 
ReadWin32BaseBoard(const ComPtr<IWbemServices> & services,std::optional<std::string> * manufacturer,std::optional<std::string> * model)92 void ReadWin32BaseBoard(const ComPtr<IWbemServices>& services,
93                         std::optional<std::string>* manufacturer,
94                         std::optional<std::string>* model) {
95   static constexpr wchar_t kManufacturer[] = L"Manufacturer";
96   static constexpr wchar_t kProduct[] = L"Product";
97   static constexpr wchar_t kQueryProcessor[] =
98       L"SELECT Manufacturer,Product FROM Win32_BaseBoard";
99 
100   ComPtr<IEnumWbemClassObject> enumerator_base_board;
101   HRESULT hr = services->ExecQuery(
102       ScopedBstr(L"WQL").Get(), ScopedBstr(kQueryProcessor).Get(),
103       WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
104       &enumerator_base_board);
105   if (FAILED(hr) || !enumerator_base_board.Get())
106     return;
107 
108   ComPtr<IWbemClassObject> class_object;
109   ULONG items_returned = 0;
110   hr = enumerator_base_board->Next(WBEM_INFINITE, 1, &class_object,
111                                    &items_returned);
112   if (FAILED(hr) || !items_returned)
113     return;
114   *manufacturer = ReadStringMember(class_object, kManufacturer);
115   *model = ReadStringMember(class_object, kProduct);
116 }
117 
ReadWin32Bios(const ComPtr<IWbemServices> & services,std::optional<std::string> * bios_manufacturer,std::optional<std::string> * bios_version)118 void ReadWin32Bios(const ComPtr<IWbemServices>& services,
119                    std::optional<std::string>* bios_manufacturer,
120                    std::optional<std::string>* bios_version) {
121   static constexpr wchar_t kManufacturer[] = L"Manufacturer";
122   static constexpr wchar_t kVersion[] = L"Version";
123   static constexpr wchar_t kQueryProcessor[] =
124       L"SELECT Manufacturer,Version FROM Win32_BIOS";
125 
126   ComPtr<IEnumWbemClassObject> enumerator_base_board;
127   HRESULT hr = services->ExecQuery(
128       ScopedBstr(L"WQL").Get(), ScopedBstr(kQueryProcessor).Get(),
129       WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
130       &enumerator_base_board);
131   if (FAILED(hr) || !enumerator_base_board.Get())
132     return;
133 
134   ComPtr<IWbemClassObject> class_object;
135   ULONG items_returned = 0;
136   hr = enumerator_base_board->Next(WBEM_INFINITE, 1, &class_object,
137                                    &items_returned);
138   if (FAILED(hr) || !items_returned)
139     return;
140   *bios_manufacturer = ReadStringMember(class_object, kManufacturer);
141   *bios_version = ReadStringMember(class_object, kVersion);
142 }
143 
ReadFirmwareType(std::optional<Motherboard::BiosType> * bios_type)144 void ReadFirmwareType(std::optional<Motherboard::BiosType>* bios_type) {
145   FIRMWARE_TYPE firmware_type = FirmwareTypeUnknown;
146   if (::GetFirmwareType(&firmware_type)) {
147     if (firmware_type == FirmwareTypeBios) {
148       *bios_type = Motherboard::BiosType::kLegacy;
149     } else if (firmware_type == FirmwareTypeUefi) {
150       *bios_type = Motherboard::BiosType::kUefi;
151     } else {
152       *bios_type = std::nullopt;
153     }
154   }
155 }
156 
ReadMotherboardDetails()157 MotherboardDetails ReadMotherboardDetails() {
158   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
159                                                 base::BlockingType::MAY_BLOCK);
160   ComPtr<IWbemServices> services;
161   MotherboardDetails details;
162   if (!base::win::CreateLocalWmiConnection(true, &services))
163     return details;
164   ReadWin32BaseBoard(services, &details.manufacturer, &details.model);
165   ReadWin32Bios(services, &details.bios_manufacturer, &details.bios_version);
166   ReadFirmwareType(&details.bios_type);
167   return details;
168 }
169 #endif
170 }  // namespace
171 
172 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
Motherboard()173 Motherboard::Motherboard() {
174   const auto details = ReadMotherboardDetails();
175   manufacturer_ = std::move(details.manufacturer),
176   model_ = std::move(details.model),
177   bios_manufacturer_ = std::move(details.bios_manufacturer),
178   bios_version_ = std::move(details.bios_version),
179   bios_type_ = std::move(details.bios_type);
180 }
181 #else
182 Motherboard::Motherboard() = default;
183 #endif
184 
185 Motherboard::~Motherboard() = default;
186 
187 }  // namespace metrics
188