LinuxでAndroidを動かすWaydroidの中で、テンキーの数字が打てなくなった。キーボードをGboardの「日本語」にすると打てて、「English (US)」にすると打てない。上段の数字キーは、どちらでも打てる。ホスト(Arch Linux、sway)でテンキーを打つと、数字が出る。

swayの設定に input * xkb_numlock enabled を足したが、直らなかった。なぜ効かないのかを、ソースと実測で調べた。

先に結論

  • ホストのNumLockの状態は、Waydroidの中のAndroidに渡らない。swayの設定は、Waydroidには効かない。
  • AndroidのNumLockは、Waydroidの中で、入った直後に消されている可能性が高い。ソースの読みと実測が合っているが、消す判定の値そのものは、見ていない。
  • Androidのアプリで、テンキーを数字に割り当てるキーレイアウトを足したら、英語のままテンキーが打てた。

ソースで分かったこと

読んだのは、WaydroidのHWC(android_hardware_waydroidのhwcomposer/wayland-hwc.cpp)と、Android 13(android13-release)の入力まわり、Waydroidが当てるパッチ(android_vendor_waydroid)。

1. ホストのキーは、キーコードのまま渡される。修飾キーの状態は渡されない。 HWCは、Waydroidのウィンドウに入ったキー入力を、キーコードの形で、Android側のパイプ(/dev/input/wl_keyboard_events)に書く。Waylandの修飾キーの状態を受け取る関数 keyboard_handle_modifiers は、中身が空になっている。ホストのNumLockがオンでも、Androidはそれを知らない。

2. テンキーの数字は、AndroidのNumLockがオンのときだけ出る。 Android 13の Generic.kcm では、NUMPAD_1 は、base: fallback MOVE_END、numlock: '1' と定義されている。NumLockがオフなら、Endキーになる。

3. NumLockの状態は、キーを離したときに切り替わる。 Keyboard.cpp の toggleLockedMetaState は、キーを押したときは何もせず、離したときに、状態を反転する。

4. Waydroidが作る入力機器は、出せるキーを2つしか登録していない。 WaydroidのEventHubのパッチ(Android 13用の 0004-EventHub-Add-wayland-inputs-support.patch)は、wayland_keyboard の keyBitmask に、BTN_MISC と KEY_OK だけを登録する。 AndroidのEventHubの markSupportedKeyCodes は、キーレイアウトにそのキーがあっても、この登録に無いキーは、「機器に無いキー」と判定する。KeyboardInputMapper::updateLedState は、NumLockのキーが無い機器について、NumLockの状態を消す。 NumLock(コード69)は、この登録に入っていない。

実測

dumpsys input で、入力機器の状態を取った。キー入力の履歴を含まない部分だけを、次のように取り出せる。

        sudo waydroid shell dumpsys input | sed -n '/Input Reader State/,/Input Dispatcher State/p' > out-1.txt
    

wayland_keyboard の項目の、MetaState(修飾キーとロックの状態)と、DownTime(最後にキーが押された時刻)を、見た。

操作DownTimeMetaState
最初776391554710000x0
a を押す781768744360000x0
NumLockを押して離す781987405550000x0

NumLockを押すと、DownTime は変わる。キー入力は、wayland_keyboard に届いている。それなのに、MetaState は 0x0 のまま。NumLockがオンになるなら、0x200000 のビットが立つはずで、立たなかった。キー入力は届いて、状態だけが残らない。これが、上の4の読みと合う。

機器の一覧(Event Hub State)には、wayland_keyboard が Generic.kl と Generic.kcm を使っていると出ていた。キーレイアウトに、NUM_LOCKの定義はある。

確認できていないこと

  • 4の判定(NUM_LOCKが登録に無いので、NumLockの状態が消える)が、直接の原因かどうか。コードの読みと、MetaState が 0x0 のままという結果からの推論で、判定の値は見ていない。
  • 日本語のときに、テンキーが打てる理由。Gboardの日本語入力が、テンキーのキーコードを自分で数字として扱っているのだと思うが、Gboardは非公開で、確認できない。
  • 入れたAndroidイメージのパッチが、読んだパッチと同じ内容か。Androidのバージョンが13であることだけを、確認した。

同じ症状は、Waydroidの Issue #1944(2025年6月、未解決)にも出ている。

直し方

Waydroidのパッチを直すには、Androidのイメージを作り直す必要がある。代わりに、Androidのアプリで、キーレイアウトを足した。android-numpad-klが、テンキーのキー(スキャンコード71〜73、75〜77、79〜83)を、NumLockに関係なく、数字のキーに割り当てるレイアウトを提供している。別のIssueのコメントで見つけた。

入れる前に、ソースを全部読んだ。Javaは4ファイル、約500行で、権限は RECEIVE_BOOT_COMPLETED だけ。通信とコマンド実行は、見つからなかった。

ビルドは、配布のAPKを使わず、ソースからやった。元のビルド設定はGradleとJDK 17を要求するが、手元にはAndroid SDK(platform 36)とJDK 27しか無い。aapt2、javac、d8、zipalign、apksigner を順に呼ぶ、短いスクリプトで組んだ。なお、/opt/android-sdk/build-tools の実行ファイルは、rootしか実行できない権限だったので、コピーして使った。

入れるときに詰まったこと

  • waydroid app install は、結果を返さない。 ソースを読むと、Android側にインストールを始めるよう伝えるだけで、結果を受け取る処理が無い。入らなくても、何も表示されない。失敗の理由は、sudo waydroid shell -- logcat -d で見た(-- が要る)。
  • python3 がpyenvの版だと、waydroid が動かない。 dbus を読み込めない。/usr/bin/python3 /bin/waydroid ... と、システムのPythonを指定した。
  • Play プロテクトが止めた。 元の設定の targetSdk を 31 にすると、「安全ではないアプリをブロックしました(Androidの以前のバージョンを対象としている)」と出て、「OK」しか選べない。35にすると、「アプリのスキャンをおすすめします」に変わった。
  • ダイアログが、一瞬で消えた。 マウスのフォーカスをWaydroidのウィンドウに移すと、消える。HWCの keyboard_handle_enter を読むと、ウィンドウにフォーカスが入るたびに、Androidの画面を切り替えている。そのためだと思うが、これは確認していない。先にフォーカスをWaydroidに置いてから、sleep 8; waydroid app install ... を実行し、ダイアログに答えた。

最後に、Androidの設定の「物理キーボード」で、wayland_keyboard に、「Full keyboard with numpad, US QWERTY」を選ぶと、英語のままテンキーで数字が打てた。

手元に残したもの

ビルドのスクリプトと、できたAPK、手順は、~/repos/waydroid-numpad-kl に置いた。ホストの xkb_numlock enabled は、Waydroidには効かないと分かったが、ほかのアプリには効いている可能性があるので、消していない。