Add a channel to query the engine keyboard state (#122885)

## Description

This PR adds a new channel to query the engine keyboard state.
See https://github.com/flutter/flutter/issues/87391#issuecomment-1228975571 for motivation. 

## Related Issue

Framework side implementation for https://github.com/flutter/flutter/issues/87391.

Once approved the framework will try to query the initial keyboard state from the engine. PRs will be needed on the engine side to answer the framework query.

## Tests

Adds 1 test.
diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart
index 8a788d9..75e9969 100644
--- a/packages/flutter/lib/src/services/binding.dart
+++ b/packages/flutter/lib/src/services/binding.dart
@@ -68,8 +68,10 @@
   void _initKeyboard() {
     _keyboard = HardwareKeyboard();
     _keyEventManager = KeyEventManager(_keyboard, RawKeyboard.instance);
-    platformDispatcher.onKeyData = _keyEventManager.handleKeyData;
-    SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage);
+    _keyboard.syncKeyboardState().then((_) {
+      platformDispatcher.onKeyData = _keyEventManager.handleKeyData;
+      SystemChannels.keyEvent.setMessageHandler(_keyEventManager.handleRawKeyMessage);
+    });
   }
 
   /// The default instance of [BinaryMessenger].
diff --git a/packages/flutter/lib/src/services/hardware_keyboard.dart b/packages/flutter/lib/src/services/hardware_keyboard.dart
index 17febb1..414b9c8 100644
--- a/packages/flutter/lib/src/services/hardware_keyboard.dart
+++ b/packages/flutter/lib/src/services/hardware_keyboard.dart
@@ -8,6 +8,7 @@
 
 import 'binding.dart';
 import 'raw_keyboard.dart';
+import 'system_channels.dart';
 
 export 'dart:ui' show KeyData;
 
@@ -494,6 +495,20 @@
     }
   }
 
+  /// Query the engine and update _pressedKeys accordingly to the engine answer.
+  Future<void> syncKeyboardState() async {
+    final Map<int, int>? keyboardState = await SystemChannels.keyboard.invokeMapMethod<int, int>(
+      'getKeyboardState',
+    );
+    if (keyboardState != null) {
+      for (final int key in keyboardState.keys) {
+        final PhysicalKeyboardKey physicalKey = PhysicalKeyboardKey(key);
+        final LogicalKeyboardKey logicalKey = LogicalKeyboardKey(keyboardState[key]!);
+        _pressedKeys[physicalKey] = logicalKey;
+      }
+    }
+  }
+
   bool _dispatchKeyEvent(KeyEvent event) {
     // This dispatching could have used the same algorithm as [ChangeNotifier],
     // but since 1) it shouldn't be necessary to support reentrantly
diff --git a/packages/flutter/lib/src/services/system_channels.dart b/packages/flutter/lib/src/services/system_channels.dart
index de9ba71..fd58eab 100644
--- a/packages/flutter/lib/src/services/system_channels.dart
+++ b/packages/flutter/lib/src/services/system_channels.dart
@@ -488,4 +488,22 @@
     'flutter/contextmenu',
     JSONMethodCodec(),
   );
+
+  /// A [MethodChannel] for retrieving keyboard pressed keys from the engine.
+  ///
+  /// The following outgoing methods are defined for this channel (invoked using
+  /// [OptionalMethodChannel.invokeMethod]):
+  ///
+  ///  * `getKeyboardState`: Obtains keyboard pressed keys from the engine.
+  ///    The keyboard state is sent as a `Map<int, int>?` where each entry
+  ///    represents a pressed keyboard key. The entry key is the physical
+  ///    key ID and the entry value is the logical key ID.
+  ///
+  /// See also:
+  ///
+  ///  * [HardwareKeyboard.syncKeyboardState], which uses this channel to synchronize
+  ///    the `HardwareKeyboard` pressed state.
+  static const MethodChannel keyboard = OptionalMethodChannel(
+    'flutter/keyboard',
+  );
 }
diff --git a/packages/flutter/test/services/binding_test.dart b/packages/flutter/test/services/binding_test.dart
index b086f1f..1aff214 100644
--- a/packages/flutter/test/services/binding_test.dart
+++ b/packages/flutter/test/services/binding_test.dart
@@ -46,7 +46,13 @@
 
   @override
   TestDefaultBinaryMessenger createBinaryMessenger() {
-    return TestDefaultBinaryMessenger(super.createBinaryMessenger());
+    Future<ByteData?> keyboardHandler(ByteData? message) async {
+      return const StandardMethodCodec().encodeSuccessEnvelope(<int, int>{1:1});
+    }
+    return TestDefaultBinaryMessenger(
+      super.createBinaryMessenger(),
+      outboundHandlers: <String, MessageHandler>{'flutter/keyboard': keyboardHandler},
+    );
   }
 }
 
@@ -145,4 +151,14 @@
     expect(result, isNotNull);
     expect(result!['response'], equals('exit'));
   });
+
+  test('initInstances synchronizes keyboard state', () async {
+    final Set<PhysicalKeyboardKey> physicalKeys = HardwareKeyboard.instance.physicalKeysPressed;
+    final Set<LogicalKeyboardKey> logicalKeys = HardwareKeyboard.instance.logicalKeysPressed;
+
+    expect(physicalKeys.length, 1);
+    expect(logicalKeys.length, 1);
+    expect(physicalKeys.first, const PhysicalKeyboardKey(1));
+    expect(logicalKeys.first, const LogicalKeyboardKey(1));
+  });
 }
diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart
index 04f836a..a85e639 100644
--- a/packages/flutter_test/lib/src/binding.dart
+++ b/packages/flutter_test/lib/src/binding.dart
@@ -107,7 +107,13 @@
 
   @override
   TestDefaultBinaryMessenger createBinaryMessenger() {
-    return TestDefaultBinaryMessenger(super.createBinaryMessenger());
+    Future<ByteData?> keyboardHandler(ByteData? message) async {
+      return const StandardMethodCodec().encodeSuccessEnvelope(<int, int>{});
+    }
+    return TestDefaultBinaryMessenger(
+      super.createBinaryMessenger(),
+      outboundHandlers: <String, MessageHandler>{'flutter/keyboard': keyboardHandler},
+    );
   }
 }
 
diff --git a/packages/flutter_test/lib/src/test_default_binary_messenger.dart b/packages/flutter_test/lib/src/test_default_binary_messenger.dart
index 3d84112..f5c4e2f 100644
--- a/packages/flutter_test/lib/src/test_default_binary_messenger.dart
+++ b/packages/flutter_test/lib/src/test_default_binary_messenger.dart
@@ -49,7 +49,12 @@
   /// Creates a [TestDefaultBinaryMessenger] instance.
   ///
   /// The [delegate] instance must not be null.
-  TestDefaultBinaryMessenger(this.delegate);
+  TestDefaultBinaryMessenger(
+    this.delegate, {
+    Map<String, MessageHandler> outboundHandlers = const <String, MessageHandler>{},
+  }) {
+    _outboundHandlers.addAll(outboundHandlers);
+  }
 
   /// The delegate [BinaryMessenger].
   final BinaryMessenger delegate;