今回は、前回の続きで、キーボードのカスタマイズの話だが、今回は、カスタマイズで修正を行うファイルと必要なツールなどについて解説する。なお、以後、アンドロイドのシェルを利用して作業をすすめる。

シェルを使うには、アンドロイドSDKの「adb shell」コマンドを使う方法があるが、Windowsの場合、コンソールがエスケープシーケンスを解釈しないので、viなどが利用できない。この問題を解決するには、アンドロイドにsshサーバーアプリをインストールして、無線LAN経由でPCからログイン(puttyなどのSSHクライアントアプリを使う)といい。USB接続してPC側のコマンドプロンプトから操作するより少し遅延を感じるものの、エスケープシーケンスが動作し、moreなどもちゃんと動作するほか、PCとアンドロイドをUSBで接続する必要がないため、USBポートをキーボードの接続に利用できるというメリットもある。ほかにWindowsのTELNETサービスを起動して自身にputtyで接続、その中でcmd.exeが動作しているので、adb shellを使うという方法もある。アンドロイドでSSHサーバーを立てるよりも反応がいいが、結局puttyなどのtelnetクライアントを使うことになり、アンドロイドとPCのUSB接続が必要というデメリットもある。

SSHサーバーは、アンドロイドマーケットにいくつか種類があるが、筆者は、Rooted SSH/SFTP DaemonをPCのPuttyから利用している。

Rooted SSH/SFTP Daemon

シェルの利用など細かく解説しているとページがいくらあっても足りないため、説明は割愛させていただく。puttyはSSHクライアントっていったすぐあとにTELNETクライアントってどういうことよ? みたいな疑問にいちいち答えていると、どれだけ文章を書いても終わらないので、この点は了承いただきたい。また、ある程度は、Linuxなど、俗に言うUnix系オペレーティングシステムの使い方も理解している必要がある。コマンドの実行を中断するにはコントロールCを使うが、そういうことも同様の理由でいちいち説明しないので、ご了承いただきたい。

USB/Buletoothキーボードの配列変更

特定のキーボード用に配列やキーを押したときに入力できる文字を定義するには、最低限、

・key layout(KLと略す)ファイル
・Key Character Map(同KCM)ファイル

を作成する。

KLファイルは、KLは、キーの配置(配列)を決めるファイルで、LKCとAKCの対応を定める。KCMファイルは、押されたキーから入力する文字を決めるファイルだ。それぞれ、標準のパスが決められており、これ以外の場所にファイルを置くには、Input Device Configuration(IDC)ファイルを作成する。

・KLファイルの場所
/system/usr/keylayout/
/data/system/devices/keylayout/

・KCMファイルの場所
/system/usr/keychars
/data/system/devices/keychars

それぞれに2つのフォルダがあるのは、通常/systemはリードオンリーでマウントされていてフォルダへのファイルの追加ができないからだ。/dataは、読み書きが可能だが、やはりroot権限が必要だ。

特定のHIDに対して前記の変換用ファイルが適用させるには、2つの方法がある。1つは、KL、KCMのファイル名をデバイスと対応付けできる形式のものにすること、もう1つはIDCファイルで変換ファイルを指定することだ。なお、IDCファイル自体は、デバイスと対応付けできるファイル名形式を利用する。

IDCは、キーボードデバイスに対しての設定を行うファイルで、

/system/usr/idc
/data/system/devices/idc

に置かれている。前述のようにIDCファイルをデバイスと対応させる方法とKL/KCMファイルを対応させる仕組みは同じなので、必ずしもIDCファイルを作る必要はない。ただし、IDCファイルではキーボードに対しての設定項目などがある。今回は話が複雑になるので、解説はしないが、利用したいという人は、以下のリンクに記述がある(英語)。

Input Device Configurationファイルについて

IDCを使わない場合(IDCが存在しないキーボード)の場合、前記のパスでKL、KCMファイルを捜すが、デバイスに適合するファイルがない場合には、デフォルトのKL、KCMファイルである「/system/usr/keylayout/Generic.kl」と「/system/usr/keychars/Generic.kcm」を利用する。なお、Android 2.3以前は、同じディレクトリにある「qwerty.kl」または「qwerty.kcm」がデフォルトの変換ファイルである。また、最初からシステム内にあるKLやKCM、IDCなどのファイルについては、絶対書き換えを行ってはならない。ファイルに矛盾があったような場合、電源キーなどを含むキーがまったく反応しなくなる可能性がある。

また、これらのファイルはテキストファイルだが、行末はLFのみ、文字コードはUTF-8(BOMなし)のUnix系OSの標準形式である。Windows側のテキスト編集アプリケーションなどで編集した場合、Windows向けに変換してしまうものがあるので、Windows側で作業するときには十分注意すること。

デバイスと変換ファイルを対応させる方法

USBおよびBluetoothデバイスには、ベンダーコード、プロダクツコードという情報があり、これで個々のデバイス(の種類)を区別している。また、改良などにより、バージョンアップした場合バージョン番号が変わるが、機能的には同一の場合とそうでないことがある。このため、デバイスに対応させる形式のファイル名には、バージョンありとなしの形式がある。同一製品でバージョンが違うHIDキーボードを同時に使い、かつ、バージョンで動作が違うことはほとんどないので、普通は、バージョンなしの形式を使う。

Input Device Configurationファイル、Key Layoutファイル、Key Character Mapファイルは、この情報を以下の形式でファイル名に組み込むことで、自動的にそのファイルを変換や設定に利用するようになる。

Vendor_XXXX_Product_YYYY.kl
Vendor_XXXX_Product_YYYY_Version_ZZZZ.kl

ただし、

XXXX → ベンダーコードの16進表現
YYYY → プロダクツコードの16進数表現
ZZZZ → バージョンコードの16進数表現

となる。これを調べるには、キーボードをUSBまたはBluetooth経由で接続しshellから以下のようなdumpsysコマンドを利用する(要root権限)。

dumpsys input | more

今回は、LogicoolのdiNovo Mini(以下diNovo)というキーボードを利用してみた。上記のコマンドを実行すると、(リスト01)のような出力があり、その中に対象とするキーボードを表す部分がある。ここにベンダーコードなどが記載されている。diNovoの場合、

Identifier: bus=0x0005, vendor=0x046d, product=0xb30c, version=0x0040

という行があり、ベンダーコードは「046d」、プロダクツコードはb30c、バージョンは0040であることがわかる。

バージョンを気にしない場合、diNovoにだけ適用されるファイルを作るには、ファイル名を、

Vendor_046d_Product_b30c

として、拡張子を「.kl」(KLファイル)、「.kcm」(KCMファイル)、「.idc」(IDCファイル)とすればよい。なお、key device configurationファイルを作る場合には、その中でKL、KCMファイルのファイル名を指定できるため、必ずしもこのファイル名を使わなくてもいい。しかし、この形式ファイル名でIDCファイルを作る必要がある。

KLファイル

アンドロイドでUSBやBluetoothのキーボードを利用する場合、実際のキー配列と入力される文字、動作が一致しないことがある。また、キーボードがアンドロイド専用でないため、ホームキーなどアンドロイドで利用するキーがないということもあるだろう。

単純にキートップと入力される文字の対応(狭い意味での配列)を変えるなら後述のKCMファイルで行えるが、そもそもそのキーボードが持っていないキーを使いたいという場合には、KLファイルを使って、キーボードドライバが送るLKCとAKCの対応関係を変える必要がある。

KLファイルはテキストファイルで、その中には、LKCとAKCの対応が定義されている。一行が1つのLKCとAKCの対応関係を表している。また、「#」から始まる行は注釈である。

key LKC AKC名 ポリシーフラグ

ここで、LKCは数値による表現(多くの場合10進数表現)とし、AKCのほうにはあらかじめシステムで割り当ててあるAKC名を使う。AKC名は、AKCに対して付けられたアルファベットなどによる名前である。また、組み込みのキーボードなどでは、キーでスリープから復帰するかどうかや、キーがメタキーであることなどを示す「ポリシーフラグ」などを指定できる。

詳しくは、以下のリンクに解説がある(英語)。

Key Layout Files

たとえば、あるキーボードがWindowsロゴキーに「0x007d」(10進数表現で125)というLKCを出したとして、これをアンドロイドのホームキーとして処理するには、

key 125 HOME

という行を作る。なおLKCは、16進数表現も可能だが、できれば、1つのKLファイル内でのLKCの表現は10進か16進のどちらかに統一する。統一すれば、番号順に並べ替えることも可能だし、重複を簡単にチェックできる。

具体的なやり方は次回解説するが、元々あるファイルからコピーを作り、これを編集してKLファイルを作る。ファイルに「key 125」という行があればこれを書き換え、なければあたらしく作る。KLでの変換は、同一のLKCについて複数の行で定義することはできないが、異なるLKCを同一のAKCに変換することは可能だ。

AKC名の調べ方

KLの書き換えで必要になるのは、AKC名だろう。LKCとAKCについては、以下のURLに対応表がある。ここには、LKCとLCK名もあるので、後述のキーイベントから押されているキーのLKCを捜すときにも利用できる。

http://source.android.com/tech/input/keyboard-devices.html

また、AKC名だけであれば、アンドロイドのAPI解説ページにある定数の表もある。以下のURLにある表Constantsの「KEYCODE_~」の部分がAKC名の定義になる。AKC名は、「KEYCODE_」部分を取ったものを使う。たとえば、「KEYCODE_HOME」なら「HOME」とする。

http://developer.android.com/reference/android/view/KeyEvent.html

なお、AKCの各コードの意味を解説した文書は、どうも存在しないようなので、その名称から推測するしかない。前述の「HOME」ならば「ホームキー」だと考えられる。資料として2つのURLを挙げたのは、列挙されているAKCに少し違いがあるからだ。たとえば、最初のURLには、カメラキーに対応すると思われる「CAMERA」はないが、2つめのURLには、定義として「KEYCODE_CAMERA」がある。また、機種によっては、メーカーが独自に定義したキーがあり、それを処理するためのAKC名も存在するようだ。システムに最初から入っているKLファイルに表にないAKC名があったら、独自に定義されたAKCであろうと推測される。こうしたキーは、該当のマシンでのみ有効で、他のマシンでは利用できるとは限らない。

KCMファイル

アプリケーションに渡されたキーイベントを入力文字に変換する場合には、KCMファイルが使われる。このKCMについては、アプリケーションが自身のリソースの中などで定義することが可能であるため、必ずしもユーザーが作ったKCMファイルの通りに変換されるとは限らない。たとえば、Googleの提供する日本語入力は、日本語用のJIS準拠配列のためのKCMリソースを内部に持っていて、これを適用することができる。

KCMファイルは、キーイベント情報に含まれるAKCとその時点でのメタキーの状態を組み合わせて、入力する文字を指定する。また、この時点で文字入力ではなく、機能を持つAKCに変換することも可能だ。KCMファイルは、KLファイルと違い1つのAKC名に対する変換を複数の行で定義する(図01)。

予約語「key」の後ろにAKC名を置き、変換のための定義行を複数ならべこれを「{」と「}」(大括弧)でくくる。このうち「label:」の部分は、アンドロイド内部で文字を表示するために使うもので、変換には直接関係しない。メタキーの状態は、定義行の先頭部分で定義しておく。アンドロイドは、(図02)のメタキー(KCMではモデファイアーという)を認識する。なお、どのキーがメタキーになるのかは、KLファイル側の定義となる。また、shift、ctrl、alt、metaについては左右の区別があり、頭に'l'、'r'を付けた場合、左側、右側のメタキーを区別してコード変換を行わせることができるが、どちらも付けなかった場合には、左右を区別せずコード変換を行う。注意するのは、どちらとも解釈できるような定義を行うとエラーとなり、変換自体が行われないことになるので注意されたい。また、ここで定義されている「fn」は、キー数の少ないコンパクトキーボードに最初から設置されているFNキーとは違うので注意されたい。通常キーボードに最初からあるFNキーはハードウェアで処理されるため、コードとして送信されることはない。KCMのモディファイアーであるfnは、同等の機能を実現するためのもので、AKCとしてはKEYCODE_FUNCTIONと呼ばれるキーである。これは、キー数が少ない場合などにメタキーとして併用する処理をアンドロイド側で行うために定義されているものだ。

変換先としては、シングルクオートで括った文字の場合には、その文字そのものが出力される。また、シングルクオートで括っていないものは機能を表し、noneは、何も文字コードを発生させないことを、fallbackは他のキーを押したことをエミュレートするものだ。

KCMについては、以下のリンクに記述がある(英語)。

key character mapファイル

キーを押したときにどのLKCが出ているかを調べる

KLを作るには、キーを押したときにLinux側がこれを処理して生成するLKCを知る必要がある。通常「A」のキーは、これに対応するLKCが発生するが、文字キー以外の機能キーがどのLKCに変換されるのかは、Input Sub system側の挙動であり、必ずしも予想できるとは限らない。実際には、HIDキーボードは、レポートとしてusageとusage Pageを発生させ、これがどのLKCに変換されるのかを調べねばならない。これは、キーボードドライバやLinux Input subsystem内部で行われる処理だ。とりあえず、Input Subsystemがどのようなイベントを発生させてアンドロイド側に伝達するのかは、geteventコマンドで観察することが可能だ。ただし、このためにはroot権限が必要となる。

geteventコマンドは、仮想的なイベントデバイス(/dev/input/eventX)が生成するイベントを表示するコマンドだ。これを起動してキーを押せば、キーの押し下げに応じたコマンドが観察できる。ただし、geteventコマンドは、イベントデバイスを指定しないと、すべてのイベントを表示してしまう。このため、あらかじめ、調べたいキーボードに対応するイベントデバイス番号を調べる必要がある。

何もオプションを指定しないでgeteventコマンドを実行すると、リスト02のような出力が出る。これをみれば、どのイベントデバイスがどのキーボードに対応しているかがわかる。今回、実験に使ったdiNovoは、/dev/input/event6に割り当てられていた。この番号は、機種や接続されている入力デバイスの数などによって変化してくるので、各自で調べるしかない。

イベント番号がわかったら、以下の形式でコマンドを起動し、キーを操作すれば、そのイベントが次々に表示されていく(図03)。注意するのは、キーのイベントは、押した時、離した時に発生し、表示上は3つのイベントが組みになっている。「-l」をオプションにすると、数値のほとんどがニモニック表示となるが、「-l」指定しなければ、数値(16進数)の表示となり、具体的なLKCを知ることができる(図04)。なお、前に示したURL「source.android.comにある表には、このLKCのニモニック表示(KEY_~)があるので、そこから調べてもいい。また、geteventのオプションは、「-h」オプションで調べることができる。

普通の文字キーならば、出てくるLKCを予想するのは難しくないが、このウィンドウズキーのようなものに関しては、実際に調べて見ないと、LKCがわからないことがある。なお、キーを押してもイベントが出ない場合、デバイスドライバで、解釈できないコードがusage/usage Pageとして送信されているか、何もコードが送信されていないと思われる。このような場合には、どうしようもなく、コードが送信されているのなら、キーボードデバイスドライバを開発すればなんとかなると思われるが、そもそもレポートが送信されていないときはどうしようもない。

これでようやくお膳立てがそろった。次回は、具体的にファイルを作成してdiNovoのキーを正しく解釈させたうえ、たとえば、Windowsロゴキーにアンドロイドのホームキーを割り当てるといったカスタマイズを行うことにする。

リスト01

# dumpsys input | more
INPUT MANAGER (dumpsys input)

Event Hub State:
  BuiltInKeyboardId: -2
  Devices:
    -1: Virtual
      Classes: 0x40000023
      Path: <virtual>
      Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd
      Location:
      UniqueId: <virtual>
      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
      KeyLayoutFile: /system/usr/keylayout/Generic.kl
      KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
     :
    省略
     :
    23: Logitech diNovo Mini
      Classes: 0x8000012b
      Path: /dev/input/event6
      Descriptor: 97dd8f6772c63f793af21b2409d71addec8256af
      Location: 9C:02:98:E4:06:9E
      UniqueId: 00:07:61:B6:6D:9A
      Identifier: bus=0x0005, vendor=0x046d, product=0xb30c, version=0x0040
      KeyLayoutFile: /data/system/devices/keylayout/Vendor_046d_Product_b30c.kl
      KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
      ConfigurationFile:
      HaveKeyboardLayoutOverlay: false
     :
    省略
     :

リスト02

# getevent
add device 1: /dev/input/event6
  name:     "Logitech diNovo Mini"
add device 2: /dev/input/event4
  name:     "lightsensor-level"
add device 3: /dev/input/event3
  name:     "proximity"
add device 4: /dev/input/event2
  name:     "tuna-gpio-keypad"
add device 5: /dev/input/event0
  name:     "barometer"
add device 6: /dev/input/event5
  name:     "Tuna Headset Jack"
add device 7: /dev/input/event1
  name:     "Melfas MMSxxx Touchscreen"
^C
#

図01

★AKC変換定義
key AKC名 {
    定義行
    定義行
     :
     :
}

★定義行
状態: 発生コード
状態: 機能

★状態
label:      キーを表示するためのラベル(文字入力自体には影響しない)
base:       メタキーがなにもない場合
number:     数値入力状態のときでメタキーが何もない場合
モデファイアー:    メタキーが押されている場合

★モデファイアー
メタキー
メタキー①+メタキー②     メタキー①と②が同時に押されている場合
メタキー①,メタキー②     メタキー①か②のどちらかが押されている場合

★機能
none        何も文字には変換しない。
fallback AKC名   AKC名のキーとして処理

★発生コード
'X'     文字X
'\\'        バックスラッシュ文字
'\n'        ニューライン文字
'\t'        タブ文字
'\''        アポストロフィー(シングルクオート)文字
'\"'        ダブルクオート(クオート)文字
'\uXXXX'    XXXXは16進数で表現したユニコードポイント
以下の4つはデッドキーで、直前の文字をアクセント記号付き文字に変換する。unicodeのcombining文字
'\u0300'        アクサングラーブ「`」
'\u0301'        アキュート「´」
'\u0302'        アクサンシルコンフレックス(サーカムフレックス)「^」
'\u0303'        チルダ「~」
'\u0308'        ウムラウト「¨」
※デッドキーとは本来タイプライタでキーを打ってもキャリッジが進まないキーのこと。これを使ってアクセント付き文字などを作る。

★例
key A {             AKC名「A」(KEYCODE_A)の変換定義
    label:      'A'     ラベル(動作には影響しない)
    base:       'a'     何もメタキーが押されてないなときには小文字aに変換
    shift, capslock:    'A'     SHIFTキーまたはCAPSLOCKキーが押されているときには大文字A
    meta:       fallback HOME   METAキーと同時押しの場合には、ホームキーとして動作する
    ctrl, alt:  none        CTRL、ALTキーが押されているときは何も出力しない
}

図1:KCMファイルは、個々のAKCに対してメタキーの有無などの条件を指定して、発生させる文字コードなどを定義する。

図02

shift    lshift,rshift   シフト(SHIFT)キー
ctrl    lctrl,rctrl     コントロール(CTRL)キー
alt lalt,ralt       ALTキー
meta    lmeta,rmeta メタキー
capslock            Caps Lockキー
fn          アンドロイドが定義するメタキーKEYCODE_FUNCTION(キーボードのFNキーとは別)
sym         記号キー
numlock         Num Lockキー
scrolllock      Scroll Lockキー

図02:KCMファイルで利用できるメタキーの一覧。PC用のキーボードを想定しているため、NUM Lockキーなどにも対応している。

図03

 # getevent -l /dev/input/event6
EV_MSC       MSC_SCAN             000700e3
EV_KEY       KEY_LEFTMETA         DOWN
EV_SYN       SYN_REPORT           00000000
EV_MSC       MSC_SCAN             000700e3
EV_KEY       KEY_LEFTMETA         UP
EV_SYN       SYN_REPORT           00000000
^C
#

図03:Linux側のInput Sub Systemから発生するイベントをモニターするのがgeteventコマンドだ。「-l」オプションを付けるとイベントコードなどがニモニック表示となる。

図04

 # getevent /dev/input/event6
0004 0004 000700e3
0001 007d 00000001
0000 0000 00000000
0004 0004 000700e3
0001 007d 00000000
0000 0000 00000000
^C
#

図04:geteventコマンドで「-l」オプションがないと、イベント内のキーコードなどが16進数で表示される。

関連リンク

連載バックナンバー