Commit aa02b4e9 authored by Ahmed Fakhry's avatar Ahmed Fakhry Committed by Commit Bot

Don't store the keyboard info when reading the layout property fails

Reading the keyboard device layout property from udev might fail. When
this happens, we used to store the keyboard info with a default layout
value = Layout1.

This CL avoid storing the keyboard info when a failure in udev is detected,
we will still fallback to the default layout for the current key event, but
we will attempt to re-read the layout property from udev on subsequent keys
until a valid layout is returned. This eliminates the need to reboot the
device to exit the bad state.

BUG=783166
TEST=added new test.

Change-Id: I94d6771ef955e20cee831ad0a93e31dcaaf8f610
Reviewed-on: https://chromium-review.googlesource.com/764444Reviewed-by: default avatarDan Erat <derat@chromium.org>
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#516063}
parent 7a20e381
...@@ -1953,6 +1953,67 @@ TEST_F(EventRewriterTest, TestRewriteFunctionKeysLayout2) { ...@@ -1953,6 +1953,67 @@ TEST_F(EventRewriterTest, TestRewriteFunctionKeysLayout2) {
CheckKeyTestCase(rewriter_, test); CheckKeyTestCase(rewriter_, test);
} }
TEST_F(EventRewriterTest, TestRewriteFunctionKeysInvalidLayout) {
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
// Not adding a keyboard simulates a failure in getting top row layout, which
// will fallback to Layout1 in which case keys are rewritten to their default
// values.
KeyTestCase invalid_layout_tests[] = {
// F2 -> Forward
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_FORWARD, ui::DomCode::BROWSER_FORWARD, ui::EF_NONE,
ui::DomKey::BROWSER_FORWARD}},
// F3 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
// F4 -> Launch App 2
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP2, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
// F7 -> Brightness up
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_BRIGHTNESS_UP, ui::DomCode::BRIGHTNESS_UP, ui::EF_NONE,
ui::DomKey::BRIGHTNESS_UP}}};
for (const auto& test : invalid_layout_tests)
CheckKeyTestCase(rewriter_, test);
// Adding a keyboard with a valid layout will take effect.
rewriter_->KeyboardDeviceAddedForTesting(
kKeyboardDeviceId, "PC Keyboard",
ui::EventRewriterChromeOS::kKbdTopRowLayout2);
KeyTestCase layout2_tests[] = {
// F2 -> Refresh
{ui::ET_KEY_PRESSED,
{ui::VKEY_F2, ui::DomCode::F2, ui::EF_NONE, ui::DomKey::F2},
{ui::VKEY_BROWSER_REFRESH, ui::DomCode::BROWSER_REFRESH, ui::EF_NONE,
ui::DomKey::BROWSER_REFRESH}},
// F3 -> Launch App 2
{ui::ET_KEY_PRESSED,
{ui::VKEY_F3, ui::DomCode::F3, ui::EF_NONE, ui::DomKey::F3},
{ui::VKEY_MEDIA_LAUNCH_APP2, ui::DomCode::ZOOM_TOGGLE, ui::EF_NONE,
ui::DomKey::ZOOM_TOGGLE}},
// F4 -> Launch App 1
{ui::ET_KEY_PRESSED,
{ui::VKEY_F4, ui::DomCode::F4, ui::EF_NONE, ui::DomKey::F4},
{ui::VKEY_MEDIA_LAUNCH_APP1, ui::DomCode::SELECT_TASK, ui::EF_NONE,
ui::DomKey::LAUNCH_MY_COMPUTER}},
// F7 -> Media Play/Pause
{ui::ET_KEY_PRESSED,
{ui::VKEY_F7, ui::DomCode::F7, ui::EF_NONE, ui::DomKey::F7},
{ui::VKEY_MEDIA_PLAY_PAUSE, ui::DomCode::MEDIA_PLAY_PAUSE, ui::EF_NONE,
ui::DomKey::MEDIA_PLAY_PAUSE}}};
for (const auto& test : layout2_tests)
CheckKeyTestCase(rewriter_, test);
}
TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) { TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) {
// Remap Search to Control. // Remap Search to Control.
chromeos::Preferences::RegisterProfilePrefs(prefs()->registry()); chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
......
...@@ -274,15 +274,15 @@ EventRewriterChromeOS::EventRewriterChromeOS( ...@@ -274,15 +274,15 @@ EventRewriterChromeOS::EventRewriterChromeOS(
EventRewriterChromeOS::~EventRewriterChromeOS() {} EventRewriterChromeOS::~EventRewriterChromeOS() {}
EventRewriterChromeOS::DeviceType void EventRewriterChromeOS::KeyboardDeviceAddedForTesting(
EventRewriterChromeOS::KeyboardDeviceAddedForTesting(
int device_id, int device_id,
const std::string& device_name, const std::string& device_name,
KeyboardTopRowLayout layout) { KeyboardTopRowLayout layout) {
// Tests must avoid XI2 reserved device IDs. // Tests must avoid XI2 reserved device IDs.
DCHECK((device_id < 0) || (device_id > 1)); DCHECK((device_id < 0) || (device_id > 1));
return KeyboardDeviceAddedInternal(device_id, device_name, kUnknownVendorId, KeyboardDeviceAddedInternal(
kUnknownProductId, layout); device_id,
GetDeviceType(device_name, kUnknownVendorId, kUnknownProductId), layout);
} }
void EventRewriterChromeOS::RewriteMouseButtonEventForTesting( void EventRewriterChromeOS::RewriteMouseButtonEventForTesting(
...@@ -346,36 +346,40 @@ void EventRewriterChromeOS::BuildRewrittenKeyEvent( ...@@ -346,36 +346,40 @@ void EventRewriterChromeOS::BuildRewrittenKeyEvent(
} }
// static // static
EventRewriterChromeOS::KeyboardTopRowLayout bool EventRewriterChromeOS::GetKeyboardTopRowLayout(
EventRewriterChromeOS::GetKeyboardTopRowLayout( const base::FilePath& device_path,
const base::FilePath& device_path) { KeyboardTopRowLayout* out_layout) {
device::ScopedUdevPtr udev(device::udev_new()); device::ScopedUdevPtr udev(device::udev_new());
if (!udev.get()) if (!udev.get())
return EventRewriterChromeOS::kKbdTopRowLayoutDefault; return false;
device::ScopedUdevDevicePtr device(device::udev_device_new_from_syspath( device::ScopedUdevDevicePtr device(device::udev_device_new_from_syspath(
udev.get(), device_path.value().c_str())); udev.get(), device_path.value().c_str()));
if (!device.get()) if (!device.get())
return EventRewriterChromeOS::kKbdTopRowLayoutDefault; return false;
const char kLayoutProperty[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT"; const char kLayoutProperty[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
std::string layout = std::string layout =
device::UdevDeviceGetPropertyValue(device.get(), kLayoutProperty); device::UdevDeviceGetPropertyValue(device.get(), kLayoutProperty);
if (layout.empty()) if (layout.empty()) {
return EventRewriterChromeOS::kKbdTopRowLayoutDefault; *out_layout = EventRewriterChromeOS::kKbdTopRowLayoutDefault;
return true;
}
int layout_id; int layout_id;
if (!base::StringToInt(layout, &layout_id)) { if (!base::StringToInt(layout, &layout_id)) {
LOG(WARNING) << "Failed to parse " << kLayoutProperty << " value '" LOG(WARNING) << "Failed to parse " << kLayoutProperty << " value '"
<< layout << "'"; << layout << "'";
return EventRewriterChromeOS::kKbdTopRowLayoutDefault; return false;
} }
if (layout_id < EventRewriterChromeOS::kKbdTopRowLayoutMin || if (layout_id < EventRewriterChromeOS::kKbdTopRowLayoutMin ||
layout_id > EventRewriterChromeOS::kKbdTopRowLayoutMax) { layout_id > EventRewriterChromeOS::kKbdTopRowLayoutMax) {
LOG(WARNING) << "Invalid " << kLayoutProperty << " '" << layout << "'"; LOG(WARNING) << "Invalid " << kLayoutProperty << " '" << layout << "'";
return EventRewriterChromeOS::kKbdTopRowLayoutDefault; return false;
} }
return static_cast<EventRewriterChromeOS::KeyboardTopRowLayout>(layout_id); *out_layout =
static_cast<EventRewriterChromeOS::KeyboardTopRowLayout>(layout_id);
return true;
} }
void EventRewriterChromeOS::DeviceKeyPressedOrReleased(int device_id) { void EventRewriterChromeOS::DeviceKeyPressedOrReleased(int device_id) {
...@@ -1165,32 +1169,13 @@ int EventRewriterChromeOS::RewriteModifierClick( ...@@ -1165,32 +1169,13 @@ int EventRewriterChromeOS::RewriteModifierClick(
return ui::EF_NONE; return ui::EF_NONE;
} }
EventRewriterChromeOS::DeviceType void EventRewriterChromeOS::KeyboardDeviceAddedInternal(
EventRewriterChromeOS::KeyboardDeviceAddedInternal(
int device_id, int device_id,
const std::string& device_name, DeviceType type,
int vendor_id,
int product_id,
KeyboardTopRowLayout layout) { KeyboardTopRowLayout layout) {
const DeviceType type = GetDeviceType(device_name, vendor_id, product_id);
if (type == kDeviceAppleKeyboard) {
VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
<< "id=" << device_id;
} else if (type == kDeviceHotrodRemote) {
VLOG(1) << "Hotrod remote '" << device_name << "' connected: "
<< "id=" << device_id;
} else if (type == kDeviceVirtualCoreKeyboard) {
VLOG(1) << "Xorg virtual '" << device_name << "' connected: "
<< "id=" << device_id;
} else {
VLOG(1) << "Unknown keyboard '" << device_name << "' connected: "
<< "id=" << device_id;
}
// Always overwrite the existing device_id since the X server may reuse a // Always overwrite the existing device_id since the X server may reuse a
// device id for an unattached device. // device id for an unattached device.
device_id_to_info_[device_id] = {type, layout}; device_id_to_info_[device_id] = {type, layout};
return type;
} }
EventRewriterChromeOS::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded( EventRewriterChromeOS::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded(
...@@ -1201,9 +1186,32 @@ EventRewriterChromeOS::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded( ...@@ -1201,9 +1186,32 @@ EventRewriterChromeOS::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded(
ui::InputDeviceManager::GetInstance()->GetKeyboardDevices(); ui::InputDeviceManager::GetInstance()->GetKeyboardDevices();
for (const auto& keyboard : keyboard_devices) { for (const auto& keyboard : keyboard_devices) {
if (keyboard.id == device_id) { if (keyboard.id == device_id) {
return KeyboardDeviceAddedInternal( const DeviceType type =
keyboard.id, keyboard.name, keyboard.vendor_id, keyboard.product_id, GetDeviceType(keyboard.name, keyboard.vendor_id, keyboard.product_id);
GetKeyboardTopRowLayout(keyboard.sys_path)); if (type == kDeviceAppleKeyboard) {
VLOG(1) << "Apple keyboard '" << keyboard.name << "' connected: "
<< "id=" << device_id;
} else if (type == kDeviceHotrodRemote) {
VLOG(1) << "Hotrod remote '" << keyboard.name << "' connected: "
<< "id=" << device_id;
} else if (type == kDeviceVirtualCoreKeyboard) {
VLOG(1) << "Xorg virtual '" << keyboard.name << "' connected: "
<< "id=" << device_id;
} else {
VLOG(1) << "Unknown keyboard '" << keyboard.name << "' connected: "
<< "id=" << device_id;
}
KeyboardTopRowLayout layout;
if (GetKeyboardTopRowLayout(keyboard.sys_path, &layout)) {
// Don't store a device info when an error occurred while reading from
// udev. This gives a chance to reattempt reading from udev on
// subsequent key events, rather than being stuck in a bad state until
// next reboot. crbug.com/783166.
KeyboardDeviceAddedInternal(keyboard.id, type, layout);
}
return type;
} }
} }
return kDeviceUnknown; return kDeviceUnknown;
......
...@@ -103,7 +103,7 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -103,7 +103,7 @@ class EventRewriterChromeOS : public ui::EventRewriter {
~EventRewriterChromeOS() override; ~EventRewriterChromeOS() override;
// Calls KeyboardDeviceAddedInternal. // Calls KeyboardDeviceAddedInternal.
DeviceType KeyboardDeviceAddedForTesting( void KeyboardDeviceAddedForTesting(
int device_id, int device_id,
const std::string& device_name, const std::string& device_name,
KeyboardTopRowLayout layout = kKbdTopRowLayoutDefault); KeyboardTopRowLayout layout = kKbdTopRowLayoutDefault);
...@@ -138,8 +138,9 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -138,8 +138,9 @@ class EventRewriterChromeOS : public ui::EventRewriter {
// Given the file path of a keyboard device, returns the layout type of the // Given the file path of a keyboard device, returns the layout type of the
// top row keys. // top row keys.
static KeyboardTopRowLayout GetKeyboardTopRowLayout( static bool GetKeyboardTopRowLayout(const base::FilePath& device_path,
const base::FilePath& device_path); KeyboardTopRowLayout* out_layout)
WARN_UNUSED_RESULT;
private: private:
struct DeviceInfo { struct DeviceInfo {
...@@ -149,15 +150,14 @@ class EventRewriterChromeOS : public ui::EventRewriter { ...@@ -149,15 +150,14 @@ class EventRewriterChromeOS : public ui::EventRewriter {
void DeviceKeyPressedOrReleased(int device_id); void DeviceKeyPressedOrReleased(int device_id);
// Adds a device to |device_id_to_type_|. // Adds a device to |device_id_to_info_| only if no failure occurs in
// retrieving the top row layout from udev, and returns the device type of
// this keyboard even if it wasn't stored in |device_id_to_info_|.
DeviceType KeyboardDeviceAdded(int device_id); DeviceType KeyboardDeviceAdded(int device_id);
// Checks the type of the |device_name|, |vendor_id| and |product_id|, and // Inserts a new entry to |device_id_to_info_|.
// inserts a new entry to |device_id_to_type_|. void KeyboardDeviceAddedInternal(int device_id,
DeviceType KeyboardDeviceAddedInternal(int device_id, DeviceType type,
const std::string& device_name,
int vendor_id,
int product_id,
KeyboardTopRowLayout layout); KeyboardTopRowLayout layout);
// Returns true if |last_keyboard_device_id_| is Apple's. // Returns true if |last_keyboard_device_id_| is Apple's.
......
...@@ -12,10 +12,11 @@ namespace ui { ...@@ -12,10 +12,11 @@ namespace ui {
bool DeviceUsesKeyboardLayout2() { bool DeviceUsesKeyboardLayout2() {
for (const InputDevice& keyboard : for (const InputDevice& keyboard :
InputDeviceManager::GetInstance()->GetKeyboardDevices()) { InputDeviceManager::GetInstance()->GetKeyboardDevices()) {
EventRewriterChromeOS::KeyboardTopRowLayout layout;
if (keyboard.type == InputDeviceType::INPUT_DEVICE_INTERNAL && if (keyboard.type == InputDeviceType::INPUT_DEVICE_INTERNAL &&
EventRewriterChromeOS::GetKeyboardTopRowLayout(keyboard.sys_path) == EventRewriterChromeOS::GetKeyboardTopRowLayout(keyboard.sys_path,
EventRewriterChromeOS::kKbdTopRowLayout2) { &layout)) {
return true; return layout == EventRewriterChromeOS::kKbdTopRowLayout2;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment