From d0ef1c1f867a6f9a6ecabec2a3f4df4b44bd9365 Mon Sep 17 00:00:00 2001 From: Bruno Leroux Date: Tue, 18 Mar 2025 07:48:21 +0100 Subject: [PATCH] [Android] Fix crash on some key repeat events (#165307) ## Description This PR fixes a crash on Android related to specific keyboard events. It is very similar to http://github.com/flutter/engine/issues/35924 which was related to down events. This PR fixes the similar logic related to repeat events. ## Related Issue Fixes [Flutter 3.29 Fatal crash with java.lang.AssertionError](https://github.com/flutter/flutter/issues/164626) ## Tests Adds 1 test. --- .../android/KeyEmbedderResponder.java | 2 +- .../android/KeyboardManagerTest.java | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java index de5af6b0ff..520f55ab6c 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java @@ -183,7 +183,7 @@ public class KeyEmbedderResponder implements KeyboardManager.Responder { postSynchronize.add( () -> synthesizeEvent( - false, key.logicalKey, key.physicalKey, event.getEventTime())); + false, key.logicalKey, eventPhysicalKey, event.getEventTime())); } preEventStates[keyIdx] = nowStates[keyIdx]; postEventAnyPressed = true; diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/KeyboardManagerTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/KeyboardManagerTest.java index 4c7b3ee3dc..2dfa602c3e 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/KeyboardManagerTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/android/KeyboardManagerTest.java @@ -1669,6 +1669,68 @@ public class KeyboardManagerTest { calls.clear(); } + // Regression test for https://github.com/flutter/flutter/issues/164626. + @Test + public void synchronizeModifiersForZeroedScanCodeOnRepeatEvent() { + // Test if ShiftLeft can be correctly synchronized during down events of + // ShiftLeft that have 0 for their metaState and 0 for their scanCode. + final KeyboardTester tester = new KeyboardTester(); + final ArrayList calls = new ArrayList<>(); + + tester.recordEmbedderCallsTo(calls); + tester.respondToTextInputWith(true); // Suppress redispatching + + // Test: repeat event when the meta state is 0 and scanCode is 0. + final KeyEvent shiftLeftKeyEvent = + new FakeKeyEvent(ACTION_DOWN, 0, KEYCODE_SHIFT_LEFT, 1, '\0', 0); + // Compute physicalKey in the same way as KeyboardManager.getPhysicalKey. + final Long shiftLeftPhysicalKey = KEYCODE_SHIFT_LEFT | KeyboardMap.kAndroidPlane; + + assertEquals(tester.keyboardManager.handleEvent(shiftLeftKeyEvent), true); + assertEquals(calls.size(), 2); + assertEmbedderEventEquals( + calls.get(0).keyData, + Type.kDown, + shiftLeftPhysicalKey, + LOGICAL_SHIFT_LEFT, + null, + false, + DeviceType.kKeyboard); + assertEmbedderEventEquals( + calls.get(1).keyData, + Type.kUp, + shiftLeftPhysicalKey, + LOGICAL_SHIFT_LEFT, + null, + true, + DeviceType.kKeyboard); + calls.clear(); + + // Similar check for AltLeft. + final KeyEvent altLeftKeyEvent = new FakeKeyEvent(ACTION_DOWN, 0, KEYCODE_ALT_LEFT, 1, '\0', 0); + final Long altLeftPhysicalKey = KEYCODE_ALT_LEFT | KeyboardMap.kAndroidPlane; + + assertEquals(tester.keyboardManager.handleEvent(altLeftKeyEvent), true); + assertEquals(calls.size(), 2); + assertEmbedderEventEquals( + calls.get(0).keyData, + Type.kDown, + altLeftPhysicalKey, + LOGICAL_ALT_LEFT, + null, + false, + DeviceType.kKeyboard); + assertEmbedderEventEquals( + calls.get(1).keyData, + Type.kUp, + altLeftPhysicalKey, + LOGICAL_ALT_LEFT, + null, + true, + DeviceType.kKeyboard); + calls.clear(); + } + @Test public void normalCapsLockEvents() { final KeyboardTester tester = new KeyboardTester();