xref: /aosp_15_r20/external/cronet/base/win/win_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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 "base/win/win_util.h"
6 
7 #include <objbase.h>
8 
9 #include <initguid.h>
10 #include <shobjidl.h>
11 #include <tchar.h>
12 
13 #include <aclapi.h>
14 #include <cfgmgr32.h>
15 #include <inspectable.h>
16 #include <lm.h>
17 #include <mdmregistration.h>
18 #include <powrprof.h>
19 #include <propkey.h>
20 #include <psapi.h>
21 #include <roapi.h>
22 #include <sddl.h>
23 #include <setupapi.h>
24 #include <shellscalingapi.h>
25 #include <signal.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <strsafe.h>
29 #include <tpcshrd.h>
30 #include <uiviewsettingsinterop.h>
31 #include <windows.ui.viewmanagement.h>
32 #include <winstring.h>
33 #include <wrl/client.h>
34 #include <wrl/wrappers/corewrappers.h>
35 
36 #include <limits>
37 #include <memory>
38 #include <optional>
39 #include <string_view>
40 #include <utility>
41 
42 #include "base/base_switches.h"
43 #include "base/command_line.h"
44 #include "base/files/file_path.h"
45 #include "base/logging.h"
46 #include "base/metrics/histogram_functions.h"
47 #include "base/notreached.h"
48 #include "base/scoped_native_library.h"
49 #include "base/strings/string_util.h"
50 #include "base/strings/string_util_win.h"
51 #include "base/strings/utf_string_conversions.h"
52 #include "base/threading/scoped_thread_priority.h"
53 #include "base/threading/thread_restrictions.h"
54 #include "base/timer/elapsed_timer.h"
55 #include "base/win/access_token.h"
56 #include "base/win/core_winrt_util.h"
57 #include "base/win/propvarutil.h"
58 #include "base/win/registry.h"
59 #include "base/win/scoped_co_mem.h"
60 #include "base/win/scoped_handle.h"
61 #include "base/win/scoped_hstring.h"
62 #include "base/win/scoped_propvariant.h"
63 #include "base/win/shlwapi.h"
64 #include "base/win/static_constants.h"
65 #include "base/win/windows_version.h"
66 
67 namespace base {
68 namespace win {
69 
70 namespace {
71 
72 // Sets the value of |property_key| to |property_value| in |property_store|.
SetPropVariantValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const ScopedPropVariant & property_value)73 bool SetPropVariantValueForPropertyStore(
74     IPropertyStore* property_store,
75     const PROPERTYKEY& property_key,
76     const ScopedPropVariant& property_value) {
77   DCHECK(property_store);
78 
79   HRESULT result = property_store->SetValue(property_key, property_value.get());
80   if (result == S_OK)
81     result = property_store->Commit();
82   if (SUCCEEDED(result))
83     return true;
84 #if DCHECK_IS_ON()
85   if (HRESULT_FACILITY(result) == FACILITY_WIN32)
86     ::SetLastError(HRESULT_CODE(result));
87   // See third_party/perl/c/i686-w64-mingw32/include/propkey.h for GUID and
88   // PID definitions.
89   DPLOG(ERROR) << "Failed to set property with GUID "
90                << WStringFromGUID(property_key.fmtid) << " PID "
91                << property_key.pid;
92 #endif
93   return false;
94 }
95 
ForceCrashOnSigAbort(int)96 void __cdecl ForceCrashOnSigAbort(int) {
97   *((volatile int*)nullptr) = 0x1337;
98 }
99 
100 // Returns the current platform role. We use the PowerDeterminePlatformRoleEx
101 // API for that.
GetPlatformRole()102 POWER_PLATFORM_ROLE GetPlatformRole() {
103   return PowerDeterminePlatformRoleEx(POWER_PLATFORM_ROLE_V2);
104 }
105 
106 // Enable V2 per-monitor high-DPI support for the process. This will cause
107 // Windows to scale dialogs, comctl32 controls, context menus, and non-client
108 // area owned by this process on a per-monitor basis. If per-monitor V2 is not
109 // available (i.e., prior to Windows 10 1703) or fails, returns false.
110 // https://docs.microsoft.com/en-us/windows/desktop/hidpi/dpi-awareness-context
EnablePerMonitorV2()111 bool EnablePerMonitorV2() {
112   if (!IsUser32AndGdi32Available())
113     return false;
114 
115   static const auto set_process_dpi_awareness_context_func =
116       reinterpret_cast<decltype(&::SetProcessDpiAwarenessContext)>(
117           GetUser32FunctionPointer("SetProcessDpiAwarenessContext"));
118   if (set_process_dpi_awareness_context_func) {
119     return set_process_dpi_awareness_context_func(
120         DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
121   }
122 
123   DCHECK_LT(GetVersion(), Version::WIN10_RS2)
124       << "SetProcessDpiAwarenessContext should be available on all platforms"
125          " >= Windows 10 Redstone 2";
126 
127   return false;
128 }
129 
GetDomainEnrollmentStateStorage()130 bool* GetDomainEnrollmentStateStorage() {
131   static bool state = IsOS(OS_DOMAINMEMBER);
132   return &state;
133 }
134 
GetRegisteredWithManagementStateStorage()135 bool* GetRegisteredWithManagementStateStorage() {
136   static bool state = []() {
137     // Mitigate the issues caused by loading DLLs on a background thread
138     // (http://crbug/973868).
139     SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
140 
141     ScopedNativeLibrary library(
142         FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
143     if (!library.is_valid())
144       return false;
145 
146     using IsDeviceRegisteredWithManagementFunction =
147         decltype(&::IsDeviceRegisteredWithManagement);
148     IsDeviceRegisteredWithManagementFunction
149         is_device_registered_with_management_function =
150             reinterpret_cast<IsDeviceRegisteredWithManagementFunction>(
151                 library.GetFunctionPointer("IsDeviceRegisteredWithManagement"));
152     if (!is_device_registered_with_management_function)
153       return false;
154 
155     BOOL is_managed = FALSE;
156     HRESULT hr =
157         is_device_registered_with_management_function(&is_managed, 0, nullptr);
158     return SUCCEEDED(hr) && is_managed;
159   }();
160 
161   return &state;
162 }
163 
164 // TODO (crbug/1300219): return a DSREG_JOIN_TYPE* instead of bool*.
GetAzureADJoinStateStorage()165 bool* GetAzureADJoinStateStorage() {
166   static bool state = []() {
167     base::ElapsedTimer timer;
168 
169     // Mitigate the issues caused by loading DLLs on a background thread
170     // (http://crbug/973868).
171     SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
172 
173     ScopedNativeLibrary netapi32(
174         base::LoadSystemLibrary(FILE_PATH_LITERAL("netapi32.dll")));
175     if (!netapi32.is_valid())
176       return false;
177 
178     const auto net_get_aad_join_information_function =
179         reinterpret_cast<decltype(&::NetGetAadJoinInformation)>(
180             netapi32.GetFunctionPointer("NetGetAadJoinInformation"));
181     if (!net_get_aad_join_information_function)
182       return false;
183 
184     const auto net_free_aad_join_information_function =
185         reinterpret_cast<decltype(&::NetFreeAadJoinInformation)>(
186             netapi32.GetFunctionPointer("NetFreeAadJoinInformation"));
187     DPCHECK(net_free_aad_join_information_function);
188 
189     DSREG_JOIN_INFO* join_info = nullptr;
190     HRESULT hr = net_get_aad_join_information_function(/*pcszTenantId=*/nullptr,
191                                                        &join_info);
192     const bool is_aad_joined = SUCCEEDED(hr) && join_info;
193     if (join_info) {
194       net_free_aad_join_information_function(join_info);
195     }
196 
197     base::UmaHistogramTimes("EnterpriseCheck.AzureADJoinStatusCheckTime",
198                             timer.Elapsed());
199     return is_aad_joined;
200   }();
201   return &state;
202 }
203 
PinUser32Internal(NativeLibraryLoadError * error)204 NativeLibrary PinUser32Internal(NativeLibraryLoadError* error) {
205   static NativeLibraryLoadError load_error;
206   static const NativeLibrary user32_module =
207       PinSystemLibrary(FILE_PATH_LITERAL("user32.dll"), &load_error);
208   if (!user32_module && error)
209     error->code = load_error.code;
210   return user32_module;
211 }
212 
213 }  // namespace
214 
215 // Uses the Windows 10 WRL API's to query the current system state. The API's
216 // we are using in the function below are supported in Win32 apps as per msdn.
217 // It looks like the API implementation is buggy at least on Surface 4 causing
218 // it to always return UserInteractionMode_Touch which as per documentation
219 // indicates tablet mode.
IsWindows10OrGreaterTabletMode(HWND hwnd)220 bool IsWindows10OrGreaterTabletMode(HWND hwnd) {
221   if (GetVersion() >= Version::WIN11) {
222     // Only Win10 supports explicit tablet mode. On Win11,
223     // get_UserInteractionMode always returns UserInteractionMode_Mouse, so
224     // instead we check if we're in slate mode or not - 0 value means slate
225     // mode. See
226     // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-gpiobuttons-convertibleslatemode
227 
228     constexpr int kKeyboardPresent = 1;
229     base::win::RegKey registry_key(
230         HKEY_LOCAL_MACHINE,
231         L"System\\CurrentControlSet\\Control\\PriorityControl", KEY_READ);
232     DWORD slate_mode = 0;
233     bool value_exists = registry_key.ReadValueDW(L"ConvertibleSlateMode",
234                                                  &slate_mode) == ERROR_SUCCESS;
235     // Some devices don't set the reg key to 1 for keyboard-only devices, so
236     // also check if the device is used as a tablet if it is not 1. Some devices
237     // don't set the registry key at all; fall back to checking if the device
238     // is used as a tablet for them as well.
239     return !(value_exists && slate_mode == kKeyboardPresent) &&
240            IsDeviceUsedAsATablet(/*reason=*/nullptr);
241   }
242 
243   ScopedHString view_settings_guid = ScopedHString::Create(
244       RuntimeClass_Windows_UI_ViewManagement_UIViewSettings);
245   Microsoft::WRL::ComPtr<IUIViewSettingsInterop> view_settings_interop;
246   HRESULT hr = ::RoGetActivationFactory(view_settings_guid.get(),
247                                         IID_PPV_ARGS(&view_settings_interop));
248   if (FAILED(hr))
249     return false;
250 
251   Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IUIViewSettings>
252       view_settings;
253   hr = view_settings_interop->GetForWindow(hwnd, IID_PPV_ARGS(&view_settings));
254   if (FAILED(hr))
255     return false;
256 
257   ABI::Windows::UI::ViewManagement::UserInteractionMode mode =
258       ABI::Windows::UI::ViewManagement::UserInteractionMode_Mouse;
259   view_settings->get_UserInteractionMode(&mode);
260   return mode == ABI::Windows::UI::ViewManagement::UserInteractionMode_Touch;
261 }
262 
263 // Returns true if a physical keyboard is detected on Windows 8 and up.
264 // Uses the Setup APIs to enumerate the attached keyboards and returns true
265 // if the keyboard count is 1 or more.. While this will work in most cases
266 // it won't work if there are devices which expose keyboard interfaces which
267 // are attached to the machine.
IsKeyboardPresentOnSlate(HWND hwnd,std::string * reason)268 bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
269   bool result = false;
270 
271   if (CommandLine::ForCurrentProcess()->HasSwitch(
272           switches::kDisableUsbKeyboardDetect)) {
273     if (reason) {
274       *reason = "Detection disabled";
275     }
276     return false;
277   }
278 
279   // This function should be only invoked for machines with touch screens.
280   if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
281       NID_INTEGRATED_TOUCH) {
282     if (!reason) {
283       return true;
284     }
285 
286     *reason += "NID_INTEGRATED_TOUCH\n";
287     result = true;
288   }
289 
290   // If it is a tablet device we assume that there is no keyboard attached.
291   if (IsTabletDevice(reason, hwnd)) {
292     if (reason) {
293       *reason += "Tablet device.\n";
294     }
295     return false;
296   }
297 
298   if (!reason) {
299     return true;
300   }
301 
302   *reason += "Not a tablet device";
303   result = true;
304 
305   // To determine whether a keyboard is present on the device, we do the
306   // following:-
307   // 1. Check whether the device supports auto rotation. If it does then
308   //    it possibly supports flipping from laptop to slate mode. If it
309   //    does not support auto rotation, then we assume it is a desktop
310   //    or a normal laptop and assume that there is a keyboard.
311 
312   // 2. If the device supports auto rotation, then we get its platform role
313   //    and check the system metric SM_CONVERTIBLESLATEMODE to see if it is
314   //    being used in slate mode. If yes then we return false here to ensure
315   //    that the OSK is displayed.
316 
317   // 3. If step 1 and 2 fail then we check attached keyboards and return true
318   //    if we find ACPI\* or HID\VID* keyboards.
319 
320   using GetAutoRotationState = decltype(&::GetAutoRotationState);
321   static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
322       GetUser32FunctionPointer("GetAutoRotationState"));
323   if (get_rotation_state) {
324     AR_STATE auto_rotation_state = AR_ENABLED;
325     get_rotation_state(&auto_rotation_state);
326     if ((auto_rotation_state & AR_NOSENSOR) ||
327         (auto_rotation_state & AR_NOT_SUPPORTED)) {
328       // If there is no auto rotation sensor or rotation is not supported in
329       // the current configuration, then we can assume that this is a desktop
330       // or a traditional laptop.
331       if (!reason) {
332         return true;
333       }
334 
335       *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
336                                                      : "AR_NOT_SUPPORTED\n";
337       result = true;
338     }
339   }
340 
341   const GUID KEYBOARD_CLASS_GUID = {
342       0x4D36E96B,
343       0xE325,
344       0x11CE,
345       {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};
346 
347   // Query for all the keyboard devices.
348   HDEVINFO device_info = SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
349                                              nullptr, DIGCF_PRESENT);
350   if (device_info == INVALID_HANDLE_VALUE) {
351     if (reason) {
352       *reason += "No keyboard info\n";
353     }
354     return result;
355   }
356 
357   // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
358   // the count is more than 1 we assume that a keyboard is present. This is
359   // under the assumption that there will always be one keyboard device.
360   for (DWORD i = 0;; ++i) {
361     SP_DEVINFO_DATA device_info_data = {0};
362     device_info_data.cbSize = sizeof(device_info_data);
363     if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data))
364       break;
365 
366     // Get the device ID.
367     wchar_t device_id[MAX_DEVICE_ID_LEN];
368     CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id,
369                                         MAX_DEVICE_ID_LEN, 0);
370     if (status == CR_SUCCESS) {
371       // To reduce the scope of the hack we only look for ACPI and HID\\VID
372       // prefixes in the keyboard device ids.
373       if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
374           StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
375         if (reason) {
376           *reason += "device: ";
377           *reason += WideToUTF8(device_id);
378           *reason += '\n';
379         }
380         // The heuristic we are using is to check the count of keyboards and
381         // return true if the API's report one or more keyboards. Please note
382         // that this will break for non keyboard devices which expose a
383         // keyboard PDO.
384         result = true;
385       }
386     }
387   }
388   return result;
389 }
390 
391 static bool g_crash_on_process_detach = false;
392 
GetUserSidString(std::wstring * user_sid)393 bool GetUserSidString(std::wstring* user_sid) {
394   std::optional<AccessToken> token = AccessToken::FromCurrentProcess();
395   if (!token)
396     return false;
397   std::optional<std::wstring> sid_string = token->User().ToSddlString();
398   if (!sid_string)
399     return false;
400   *user_sid = *sid_string;
401   return true;
402 }
403 
404 class ScopedAllowBlockingForUserAccountControl : public ScopedAllowBlocking {};
405 
UserAccountControlIsEnabled()406 bool UserAccountControlIsEnabled() {
407   // This can be slow if Windows ends up going to disk.  Should watch this key
408   // for changes and only read it once, preferably on the file thread.
409   //   http://code.google.com/p/chromium/issues/detail?id=61644
410   ScopedAllowBlockingForUserAccountControl allow_blocking;
411 
412   RegKey key(HKEY_LOCAL_MACHINE,
413              L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
414              KEY_READ);
415   DWORD uac_enabled;
416   if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) {
417     return true;
418   }
419   // Users can set the EnableLUA value to something arbitrary, like 2, which
420   // Vista will treat as UAC enabled, so we make sure it is not set to 0.
421   return (uac_enabled != 0);
422 }
423 
SetBooleanValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,bool property_bool_value)424 bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
425                                      const PROPERTYKEY& property_key,
426                                      bool property_bool_value) {
427   ScopedPropVariant property_value;
428   if (FAILED(InitPropVariantFromBoolean(property_bool_value,
429                                         property_value.Receive()))) {
430     return false;
431   }
432 
433   return SetPropVariantValueForPropertyStore(property_store, property_key,
434                                              property_value);
435 }
436 
SetStringValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const wchar_t * property_string_value)437 bool SetStringValueForPropertyStore(IPropertyStore* property_store,
438                                     const PROPERTYKEY& property_key,
439                                     const wchar_t* property_string_value) {
440   ScopedPropVariant property_value;
441   if (FAILED(InitPropVariantFromString(property_string_value,
442                                        property_value.Receive()))) {
443     return false;
444   }
445 
446   return SetPropVariantValueForPropertyStore(property_store, property_key,
447                                              property_value);
448 }
449 
SetClsidForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const CLSID & property_clsid_value)450 bool SetClsidForPropertyStore(IPropertyStore* property_store,
451                               const PROPERTYKEY& property_key,
452                               const CLSID& property_clsid_value) {
453   ScopedPropVariant property_value;
454   if (FAILED(InitPropVariantFromCLSID(property_clsid_value,
455                                       property_value.Receive()))) {
456     return false;
457   }
458 
459   return SetPropVariantValueForPropertyStore(property_store, property_key,
460                                              property_value);
461 }
462 
SetAppIdForPropertyStore(IPropertyStore * property_store,const wchar_t * app_id)463 bool SetAppIdForPropertyStore(IPropertyStore* property_store,
464                               const wchar_t* app_id) {
465   // App id should be less than 128 chars and contain no space. And recommended
466   // format is CompanyName.ProductName[.SubProduct.ProductNumber].
467   // See
468   // https://docs.microsoft.com/en-us/windows/win32/shell/appids#how-to-form-an-application-defined-appusermodelid
469   DCHECK_LT(lstrlen(app_id), 128);
470   DCHECK_EQ(wcschr(app_id, L' '), nullptr);
471 
472   return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID,
473                                         app_id);
474 }
475 
476 static const wchar_t kAutoRunKeyPath[] =
477     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
478 
AddCommandToAutoRun(HKEY root_key,const std::wstring & name,const std::wstring & command)479 bool AddCommandToAutoRun(HKEY root_key,
480                          const std::wstring& name,
481                          const std::wstring& command) {
482   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
483   return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
484           ERROR_SUCCESS);
485 }
486 
RemoveCommandFromAutoRun(HKEY root_key,const std::wstring & name)487 bool RemoveCommandFromAutoRun(HKEY root_key, const std::wstring& name) {
488   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
489   return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
490 }
491 
ReadCommandFromAutoRun(HKEY root_key,const std::wstring & name,std::wstring * command)492 bool ReadCommandFromAutoRun(HKEY root_key,
493                             const std::wstring& name,
494                             std::wstring* command) {
495   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
496   return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
497 }
498 
SetShouldCrashOnProcessDetach(bool crash)499 void SetShouldCrashOnProcessDetach(bool crash) {
500   g_crash_on_process_detach = crash;
501 }
502 
ShouldCrashOnProcessDetach()503 bool ShouldCrashOnProcessDetach() {
504   return g_crash_on_process_detach;
505 }
506 
SetAbortBehaviorForCrashReporting()507 void SetAbortBehaviorForCrashReporting() {
508   // Prevent CRT's abort code from prompting a dialog or trying to "report" it.
509   // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
510   // has the sideffect of clearing our exception filter, which means we
511   // don't get any crash.
512   _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
513 
514   // Set a SIGABRT handler for good measure. We will crash even if the default
515   // is left in place, however this allows us to crash earlier. And it also
516   // lets us crash in response to code which might directly call raise(SIGABRT)
517   signal(SIGABRT, ForceCrashOnSigAbort);
518 }
519 
IsTabletDevice(std::string * reason,HWND hwnd)520 bool IsTabletDevice(std::string* reason, HWND hwnd) {
521   if (IsWindows10OrGreaterTabletMode(hwnd))
522     return true;
523 
524   return IsDeviceUsedAsATablet(reason);
525 }
526 
527 // This method is used to set the right interactions media queries,
528 // see https://drafts.csswg.org/mediaqueries-4/#mf-interaction. It doesn't
529 // check the Windows 10 tablet mode because it doesn't reflect the actual
530 // input configuration of the device and can be manually triggered by the user
531 // independently from the hardware state.
IsDeviceUsedAsATablet(std::string * reason)532 bool IsDeviceUsedAsATablet(std::string* reason) {
533   // Once this is set, it shouldn't be overridden, and it should be the ultimate
534   // return value, so that this method returns the same result whether or not
535   // reason is NULL.
536   std::optional<bool> ret;
537 
538   if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) {
539     if (!reason) {
540       return false;
541     }
542 
543     *reason += "Device does not support touch.\n";
544     ret = false;
545   }
546 
547   // If the device is docked, the user is treating the device as a PC.
548   if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
549     if (!reason) {
550       return false;
551     }
552 
553     *reason += "SM_SYSTEMDOCKED\n";
554     if (!ret.has_value()) {
555       ret = false;
556     }
557   }
558 
559   // If the device is not supporting rotation, it's unlikely to be a tablet,
560   // a convertible or a detachable.
561   // See
562   // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
563   using GetAutoRotationStateType = decltype(GetAutoRotationState)*;
564   static const auto get_auto_rotation_state_func =
565       reinterpret_cast<GetAutoRotationStateType>(
566           GetUser32FunctionPointer("GetAutoRotationState"));
567   if (get_auto_rotation_state_func) {
568     AR_STATE rotation_state = AR_ENABLED;
569     if (get_auto_rotation_state_func(&rotation_state) &&
570         (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR)) != 0) {
571       return ret.value_or(false);
572     }
573   }
574 
575   // PlatformRoleSlate was added in Windows 8+.
576   POWER_PLATFORM_ROLE role = GetPlatformRole();
577   bool is_tablet = false;
578   if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
579     is_tablet = !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
580     if (!is_tablet) {
581       if (!reason) {
582         return false;
583       }
584 
585       *reason += "Not in slate mode.\n";
586       if (!ret.has_value()) {
587         ret = false;
588       }
589     } else if (reason) {
590       *reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n"
591                                               : "PlatformRoleSlate\n";
592     }
593   } else if (reason) {
594     *reason += "Device role is not mobile or slate.\n";
595   }
596   return ret.value_or(is_tablet);
597 }
598 
IsEnrolledToDomain()599 bool IsEnrolledToDomain() {
600   return *GetDomainEnrollmentStateStorage();
601 }
602 
IsDeviceRegisteredWithManagement()603 bool IsDeviceRegisteredWithManagement() {
604   // GetRegisteredWithManagementStateStorage() can be true for devices running
605   // the Home sku, however the Home sku does not allow for management of the web
606   // browser. As such, we automatically exclude devices running the Home sku.
607   if (OSInfo::GetInstance()->version_type() == SUITE_HOME)
608     return false;
609   return *GetRegisteredWithManagementStateStorage();
610 }
611 
IsJoinedToAzureAD()612 bool IsJoinedToAzureAD() {
613   return *GetAzureADJoinStateStorage();
614 }
615 
IsUser32AndGdi32Available()616 bool IsUser32AndGdi32Available() {
617   static const bool is_user32_and_gdi32_available = []() {
618     // If win32k syscalls aren't disabled, then user32 and gdi32 are available.
619     PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
620     if (::GetProcessMitigationPolicy(GetCurrentProcess(),
621                                      ProcessSystemCallDisablePolicy, &policy,
622                                      sizeof(policy))) {
623       return policy.DisallowWin32kSystemCalls == 0;
624     }
625 
626     return true;
627   }();
628   return is_user32_and_gdi32_available;
629 }
630 
GetLoadedModulesSnapshot(HANDLE process,std::vector<HMODULE> * snapshot)631 bool GetLoadedModulesSnapshot(HANDLE process, std::vector<HMODULE>* snapshot) {
632   DCHECK(snapshot);
633   DCHECK_EQ(0u, snapshot->size());
634   snapshot->resize(128);
635 
636   // We will retry at least once after first determining |bytes_required|. If
637   // the list of modules changes after we receive |bytes_required| we may retry
638   // more than once.
639   int retries_remaining = 5;
640   do {
641     DWORD bytes_required = 0;
642     // EnumProcessModules returns 'success' even if the buffer size is too
643     // small.
644     DCHECK_GE(std::numeric_limits<DWORD>::max(),
645               snapshot->size() * sizeof(HMODULE));
646     if (!::EnumProcessModules(
647             process, &(*snapshot)[0],
648             static_cast<DWORD>(snapshot->size() * sizeof(HMODULE)),
649             &bytes_required)) {
650       DPLOG(ERROR) << "::EnumProcessModules failed.";
651       return false;
652     }
653 
654     DCHECK_EQ(0u, bytes_required % sizeof(HMODULE));
655     size_t num_modules = bytes_required / sizeof(HMODULE);
656     if (num_modules <= snapshot->size()) {
657       // Buffer size was too big, presumably because a module was unloaded.
658       snapshot->erase(snapshot->begin() + static_cast<ptrdiff_t>(num_modules),
659                       snapshot->end());
660       return true;
661     }
662 
663     if (num_modules == 0) {
664       DLOG(ERROR) << "Can't determine the module list size.";
665       return false;
666     }
667 
668     // Buffer size was too small. Try again with a larger buffer. A little
669     // more room is given to avoid multiple expensive calls to
670     // ::EnumProcessModules() just because one module has been added.
671     snapshot->resize(num_modules + 8, nullptr);
672   } while (--retries_remaining);
673 
674   DLOG(ERROR) << "Failed to enumerate modules.";
675   return false;
676 }
677 
EnableFlicks(HWND hwnd)678 void EnableFlicks(HWND hwnd) {
679   ::RemoveProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY);
680 }
681 
DisableFlicks(HWND hwnd)682 void DisableFlicks(HWND hwnd) {
683   ::SetProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY,
684             reinterpret_cast<HANDLE>(TABLET_DISABLE_FLICKS |
685                                      TABLET_DISABLE_FLICKFALLBACKKEYS));
686 }
687 
EnableHighDPISupport()688 void EnableHighDPISupport() {
689   if (!IsUser32AndGdi32Available())
690     return;
691 
692   // Enable per-monitor V2 if it is available (Win10 1703 or later).
693   if (EnablePerMonitorV2())
694     return;
695 
696   // Fall back to per-monitor DPI for older versions of Win10.
697   PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_PER_MONITOR_DPI_AWARE;
698   if (!::SetProcessDpiAwareness(process_dpi_awareness)) {
699     // For windows versions where SetProcessDpiAwareness fails, try its
700     // predecessor.
701     BOOL result = ::SetProcessDPIAware();
702     DCHECK(result) << "SetProcessDPIAware failed.";
703   }
704 }
705 
WStringFromGUID(const::GUID & rguid)706 std::wstring WStringFromGUID(const ::GUID& rguid) {
707   // This constant counts the number of characters in the formatted string,
708   // including the null termination character.
709   constexpr int kGuidStringCharacters =
710       1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1;
711   wchar_t guid_string[kGuidStringCharacters];
712   CHECK(SUCCEEDED(StringCchPrintfW(
713       guid_string, kGuidStringCharacters,
714       L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid.Data1,
715       rguid.Data2, rguid.Data3, rguid.Data4[0], rguid.Data4[1], rguid.Data4[2],
716       rguid.Data4[3], rguid.Data4[4], rguid.Data4[5], rguid.Data4[6],
717       rguid.Data4[7])));
718   return std::wstring(guid_string, kGuidStringCharacters - 1);
719 }
720 
PinUser32(NativeLibraryLoadError * error)721 bool PinUser32(NativeLibraryLoadError* error) {
722   return PinUser32Internal(error) != nullptr;
723 }
724 
GetUser32FunctionPointer(const char * function_name,NativeLibraryLoadError * error)725 void* GetUser32FunctionPointer(const char* function_name,
726                                NativeLibraryLoadError* error) {
727   NativeLibrary user32_module = PinUser32Internal(error);
728   if (user32_module)
729     return GetFunctionPointerFromNativeLibrary(user32_module, function_name);
730   return nullptr;
731 }
732 
GetWindowObjectName(HANDLE handle)733 std::wstring GetWindowObjectName(HANDLE handle) {
734   // Get the size of the name.
735   std::wstring object_name;
736 
737   DWORD size = 0;
738   ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
739   if (!size) {
740     DPCHECK(false);
741     return object_name;
742   }
743 
744   LOG_ASSERT(size % sizeof(wchar_t) == 0u);
745 
746   // Query the name of the object.
747   if (!::GetUserObjectInformation(
748           handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
749           size, &size)) {
750     DPCHECK(false);
751   }
752 
753   return object_name;
754 }
755 
GetPointerDevice(HANDLE device,POINTER_DEVICE_INFO & result)756 bool GetPointerDevice(HANDLE device, POINTER_DEVICE_INFO& result) {
757   return ::GetPointerDevice(device, &result);
758 }
759 
GetPointerDevices()760 std::optional<std::vector<POINTER_DEVICE_INFO>> GetPointerDevices() {
761   uint32_t device_count;
762   if (!::GetPointerDevices(&device_count, nullptr)) {
763     return std::nullopt;
764   }
765 
766   std::vector<POINTER_DEVICE_INFO> pointer_devices(device_count);
767   if (!::GetPointerDevices(&device_count, pointer_devices.data())) {
768     return std::nullopt;
769   }
770   return pointer_devices;
771 }
772 
RegisterPointerDeviceNotifications(HWND hwnd,bool notify_proximity_changes)773 bool RegisterPointerDeviceNotifications(HWND hwnd,
774                                         bool notify_proximity_changes) {
775   return ::RegisterPointerDeviceNotifications(hwnd, notify_proximity_changes);
776 }
777 
IsRunningUnderDesktopName(std::wstring_view desktop_name)778 bool IsRunningUnderDesktopName(std::wstring_view desktop_name) {
779   HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
780   if (!thread_desktop)
781     return false;
782 
783   std::wstring current_desktop_name = GetWindowObjectName(thread_desktop);
784   return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name),
785                                     AsStringPiece16(desktop_name));
786 }
787 
788 // This method is used to detect whether current session is a remote session.
789 // See:
790 // https://docs.microsoft.com/en-us/windows/desktop/TermServ/detecting-the-terminal-services-environment
IsCurrentSessionRemote()791 bool IsCurrentSessionRemote() {
792   if (::GetSystemMetrics(SM_REMOTESESSION))
793     return true;
794 
795   DWORD current_session_id = 0;
796 
797   if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id))
798     return false;
799 
800   static constexpr wchar_t kRdpSettingsKeyName[] =
801       L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
802   RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
803   if (!key.Valid())
804     return false;
805 
806   static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
807   DWORD glass_session_id = 0;
808   if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
809       ERROR_SUCCESS) {
810     return false;
811   }
812 
813   return current_session_id != glass_session_id;
814 }
815 
IsAppVerifierLoaded()816 bool IsAppVerifierLoaded() {
817   return GetModuleHandleA(kApplicationVerifierDllName);
818 }
819 
ScopedDomainStateForTesting(bool state)820 ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
821     : initial_state_(IsEnrolledToDomain()) {
822   *GetDomainEnrollmentStateStorage() = state;
823 }
824 
~ScopedDomainStateForTesting()825 ScopedDomainStateForTesting::~ScopedDomainStateForTesting() {
826   *GetDomainEnrollmentStateStorage() = initial_state_;
827 }
828 
829 ScopedDeviceRegisteredWithManagementForTesting::
ScopedDeviceRegisteredWithManagementForTesting(bool state)830     ScopedDeviceRegisteredWithManagementForTesting(bool state)
831     : initial_state_(IsDeviceRegisteredWithManagement()) {
832   *GetRegisteredWithManagementStateStorage() = state;
833 }
834 
835 ScopedDeviceRegisteredWithManagementForTesting::
~ScopedDeviceRegisteredWithManagementForTesting()836     ~ScopedDeviceRegisteredWithManagementForTesting() {
837   *GetRegisteredWithManagementStateStorage() = initial_state_;
838 }
839 
ScopedAzureADJoinStateForTesting(bool state)840 ScopedAzureADJoinStateForTesting::ScopedAzureADJoinStateForTesting(bool state)
841     : initial_state_(std::exchange(*GetAzureADJoinStateStorage(), state)) {}
842 
~ScopedAzureADJoinStateForTesting()843 ScopedAzureADJoinStateForTesting::~ScopedAzureADJoinStateForTesting() {
844   *GetAzureADJoinStateStorage() = initial_state_;
845 }
846 
847 }  // namespace win
848 }  // namespace base
849