Skip to content

Commit

Permalink
Add USB keyboard detection reporting and disable flag
Browse files Browse the repository at this point in the history
Some users are having keyboards falsely detected, so this
CL provides them a workaround flag and lists the detection
data in chrome://system for debugging purposes

BUG=491516
NOTRY=TRUE

Review URL: https://codereview.chromium.org/1191303003

Cr-Commit-Position: refs/heads/master@{#335662}
  • Loading branch information
jschuh authored and Commit bot committed Jun 23, 2015
1 parent c4c79ad commit b156d5b
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 19 deletions.
5 changes: 5 additions & 0 deletions base/base_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ const char kProfilerTiming[] = "profiler-timing";
// chrome://profiler.
const char kProfilerTimingDisabledValue[] = "0";

#if defined(OS_WIN)
// Disables the USB keyboard detection for blocking the OSK on Win8+.
const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
#endif

#if defined(OS_POSIX)
// Used for turning on Breakpad crash reporting in a debug environment where
// crash reporting is typically compiled but disabled.
Expand Down
4 changes: 4 additions & 0 deletions base/base_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ extern const char kV[];
extern const char kVModule[];
extern const char kWaitForDebugger[];

#if defined(OS_WIN)
extern const char kDisableUsbKeyboardDetect[];
#endif

#if defined(OS_POSIX)
extern const char kEnableCrashReporterForTesting[];
#endif
Expand Down
80 changes: 61 additions & 19 deletions base/win/win_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
#include <signal.h>
#include <stdlib.h>

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/metro.h"
#include "base/win/registry.h"
Expand Down Expand Up @@ -58,24 +61,44 @@ const wchar_t kWindows8OSKRegPath[] =
L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}"
L"\\LocalServer32";

} // namespace

// Returns true if a physical keyboard is detected on Windows 8 and up.
// Uses the Setup APIs to enumerate the attached keyboards and returns true
// if the keyboard count is 1 or more.. While this will work in most cases
// it won't work if there are devices which expose keyboard interfaces which
// are attached to the machine.
bool IsKeyboardPresentOnSlate() {
bool IsKeyboardPresentOnSlate(std::string* reason) {
bool result = false;

// This function is only supported for Windows 8 and up.
DCHECK(GetVersion() >= VERSION_WIN8);
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableUsbKeyboardDetect)) {
if (reason)
*reason = "Detection disabled";
return false;
}

// This function should be only invoked for machines with touch screens.
if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH)
!= NID_INTEGRATED_TOUCH) {
return true;
if (reason) {
*reason += "NID_INTEGRATED_TOUCH\n";
result = true;
} else {
return true;
}
}

// If the device is docked, the user is treating the device as a PC.
if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0)
return true;
if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
if (reason) {
*reason += "SM_SYSTEMDOCKED\n";
result = true;
} else {
return true;
}
}

// To determine whether a keyboard is present on the device, we do the
// following:-
Expand Down Expand Up @@ -106,7 +129,13 @@ bool IsKeyboardPresentOnSlate() {
// If there is no auto rotation sensor or rotation is not supported in
// the current configuration, then we can assume that this is a desktop
// or a traditional laptop.
return true;
if (reason) {
*reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n" :
"AR_NOT_SUPPORTED\n";
result = true;
} else {
return true;
}
}
}

Expand All @@ -117,8 +146,15 @@ bool IsKeyboardPresentOnSlate() {
POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole();

if (((role == PlatformRoleMobile) || (role == PlatformRoleSlate)) &&
(GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0))
return false;
(GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0)) {
if (reason) {
*reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n" :
"PlatformRoleSlate\n";
// Don't change result here if it's already true.
} else {
return false;
}
}

const GUID KEYBOARD_CLASS_GUID =
{ 0x4D36E96B, 0xE325, 0x11CE,
Expand All @@ -127,13 +163,15 @@ bool IsKeyboardPresentOnSlate() {
// Query for all the keyboard devices.
HDEVINFO device_info =
SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, NULL, NULL, DIGCF_PRESENT);
if (device_info == INVALID_HANDLE_VALUE)
return false;
if (device_info == INVALID_HANDLE_VALUE) {
if (reason)
*reason += "No keyboard info\n";
return result;
}

// Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
// the count is more than 1 we assume that a keyboard is present. This is
// under the assumption that there will always be one keyboard device.
int keyboard_count = 0;
for (DWORD i = 0;; ++i) {
SP_DEVINFO_DATA device_info_data = { 0 };
device_info_data.cbSize = sizeof(device_info_data);
Expand All @@ -151,18 +189,22 @@ bool IsKeyboardPresentOnSlate() {
// prefixes in the keyboard device ids.
if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
keyboard_count++;
if (reason) {
*reason += "device: ";
*reason += WideToUTF8(device_id);
*reason += '\n';
}
// The heuristic we are using is to check the count of keyboards and
// return true if the API's report one or more keyboards. Please note
// that this will break for non keyboard devices which expose a
// keyboard PDO.
result = true;
}
}
}
// The heuristic we are using is to check the count of keyboards and return
// true if the API's report one or more keyboards. Please note that this
// will break for non keyboard devices which expose a keyboard PDO.
return keyboard_count >= 1;
return result;
}

} // namespace

static bool g_crash_on_process_detach = false;

void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics) {
Expand Down Expand Up @@ -352,7 +394,7 @@ bool DisplayVirtualKeyboard() {
if (GetVersion() < VERSION_WIN8)
return false;

if (IsKeyboardPresentOnSlate())
if (IsKeyboardPresentOnSlate(nullptr))
return false;

static LazyInstance<string16>::Leaky osk_path = LAZY_INSTANCE_INITIALIZER;
Expand Down
5 changes: 5 additions & 0 deletions base/win/win_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ BASE_EXPORT void SetAbortBehaviorForCrashReporting();
// insight into how users use Chrome.
BASE_EXPORT bool IsTabletDevice();

// A slate is a touch device that may have a keyboard attached. This function
// returns true if a keyboard is attached and optionally will set the reason
// parameter to the detection method that was used to detect the keyboard.
BASE_EXPORT bool IsKeyboardPresentOnSlate(std::string* reason);

// Get the size of a struct up to and including the specified member.
// This is necessary to set compatible struct sizes for different versions
// of certain Windows APIs (e.g. SystemParametersInfo).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"

#if defined(OS_WIN)
#include "base/win/win_util.h"
#endif

namespace {

Expand All @@ -29,6 +32,9 @@ const char kChromeVersionTag[] = "CHROME VERSION";
#if !defined(OS_CHROMEOS)
const char kOsVersionTag[] = "OS VERSION";
#endif
#if defined(OS_WIN)
const char kUsbKeyboardDetected[] = "usb_keyboard_detected";
#endif

} // namespace

Expand Down Expand Up @@ -60,6 +66,9 @@ void ChromeInternalLogSource::Fetch(const SysLogsSourceCallback& callback) {
PopulateSyncLogs(&response);
PopulateExtensionInfoLogs(&response);
PopulateDataReductionProxyLogs(&response);
#if defined(OS_WIN)
PopulateUsbKeyboardDetected(&response);
#endif

if (ProfileManager::GetLastUsedProfile()->IsChild())
response["account_type"] = "child";
Expand Down Expand Up @@ -144,4 +153,15 @@ void ChromeInternalLogSource::PopulateDataReductionProxyLogs(
"enabled" : "disabled";
}

#if defined(OS_WIN)
void ChromeInternalLogSource::PopulateUsbKeyboardDetected(
SystemLogsResponse* response) {
std::string reason;
bool result = base::win::IsKeyboardPresentOnSlate(&reason);
(*response)[kUsbKeyboardDetected] = result ? "Keyboard Detected:\n" :
"No Keyboard:\n";
(*response)[kUsbKeyboardDetected] += reason;
}
#endif

} // namespace system_logs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class ChromeInternalLogSource : public SystemLogsSource {
void PopulateSyncLogs(SystemLogsResponse* response);
void PopulateExtensionInfoLogs(SystemLogsResponse* response);
void PopulateDataReductionProxyLogs(SystemLogsResponse* response);
#if defined(OS_WIN)
void PopulateUsbKeyboardDetected(SystemLogsResponse* response);
#endif

DISALLOW_COPY_AND_ASSIGN(ChromeInternalLogSource);
};
Expand Down

0 comments on commit b156d5b

Please sign in to comment.