オクトドン:地下深くに行く

このエッセイでは、Octdonキーボードの歴史から少し離れて、プロトタイプ開発のさまざまな段階で遭遇した実際の問題についてお話したいと思います。 高レベルのAPI(この場合はAndroid API)と自家製のハードウェアをドッキングすることがどれほど難しいかは、決してわからないことがわかります。 落とし穴はすべてのステップで待機状態にあり、このタスクまたはそのタスクがどれほど骨の折れる作業であるかを概算することさえできません。 さらに、その時間が限られていることを考慮すると、プロトタイプがすでに動作するはずの何らかの展示またはプレゼンテーションが常に存在し、解決策はあらゆる可能な方法で見つける必要があります





写真:パトリック・プルル/ AFP /ゲッティイメージズ



...そして結果はめったにエレガントではありません。



Android開発者、エレクトロニクスエンジニア、ラジオインストーラーとしてOktodonの作成に参加しています。 ここ数年、私の主な活動は.NETでの高度な開発でしたが、私の子供時代はZX Spectrumコンピューターに満足しており、それに応じてZ80プロセッサーのアセンブラーにも満足していました。 その結果、私はコンピューターが信号と時間図のレベルでどのように機能するかを理解し始めましたが、これはマークを残す以外にありませんでした-私はまだ低レベルの開発が本当に好きです。 そして、基本的に新しいキーボード、つまりハードウェアを作成するプロジェクトに参加するよう申し出られたとき、私は断ることができませんでした。



私はすべてのハックを誇りに思っていません(それ以外の名前を付けることはできません)。 しかし、彼らはプロジェクトの成長と開発を許可し、単純なアイデアではなく、利害関係者、将来のユーザー、投資家を引き付ける実用的な実装を可能にしました。 開発のこの段階では、このコードはすべて使用されなくなりました。心配しないでください:)



私たちがやっていることのより完全な図を作成するために、あなたは私たちのリーダーAlexLysenkoによって書かれた以前の投稿を読むことに興味があるでしょう。



プロトタイプNo. 1-Bluetooth



最初のプロトタイプでは、Bluetooth共有を使用しました。 キーボードと電話を接続するための便利な技術はこれ以上ないように思われ、すべてが箱から出して機能するという自信がありました。 しかし、これは起こりませんでした。

最初の問題は、バージョン2.3以降、Androidがシステム内のBTキーボードの組み込みサポートを備えていたという事実から生じました。 システムが「ペアリング」されたキーボードを検出するとすぐに、それをすぐにつかみ、入力ツールとして使用し始めました。 同じことをしようとしていたので、Okodonジョイスティックを1回クリックするだけで、「ours」と「system」という2つの文字を印刷することは避けられませんでした。 なんとなくおしゃべりなAndroidを消す必要がありました。 電話は「わだち掘れ」で、キーボード接続を無視できる最も便利な場所を見つけるために、カーネルとシステムライブラリのソースコードを掘り始めました。 この場所はlibuiライブラリであることが判明しました。

フォームのダーティハックがopen_device関数に導入されました:



if ((id.vendor == 0x05AC && id.product == 0x0239)) // VID  PID   { LOGI("Ignoring device %04x %04x", id.vendor, id.product); close(fd); fd = -1; return -1; }
      
      







どうやら、私たちは単にデバイスを開くことを許可していません。 これで、OSがキーボードを取得しなくなり、Android APIのBluetooth機能を使用してキーボードと通信できるようになりました。 そして、ここでも問題が待っていました。データを受信するためにBTソケットを開くことができませんでした-標準関数createRfcommSocketToServiceRecordにエラーが発生しました。 物語の年代記は詳細を保存しませんでしたが、問題には多くの時間がかかりました。 その結果、(それでも)マーケットの製品をリバースエンジニアリングするようになりました。それにより、このBTキーボードを、それ自体では実行できない「古い」Androidで操作できるようになりました。 難読化のバリケードの背後にあるプログラムの1つで、美しいものが見つかりました。



 private static BluetoothSocket createBluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) { try { Class[] parameterTypes = new Class[] { Integer.TYPE, // type Integer.TYPE, // fd Boolean.TYPE, // auth Boolean.TYPE, // encrypt String.class, // address Integer.TYPE // port }; Constructor<BluetoothSocket> constructor = BluetoothSocket.class.getDeclaredConstructor(parameterTypes); constructor.setAccessible(true); Object[] parameters = new Object[] { Integer.valueOf(type), Integer.valueOf(fd), Boolean.valueOf(auth), Boolean.valueOf(encrypt), address, Integer.valueOf(port), }; return (BluetoothSocket)constructor.newInstance(parameters); } catch (Exception ex) { return null; } } BluetoothSocket intrSocket = createBluetoothSocket(3 /* TYPE_L2CAP */, -1, false, false, "DC:2C:26:A0:C2:50" /* MAC-address */, 0x13 /* INTR port */);
      
      







型のプライベートコンストラクターを呼び出す! 提示されたコードが取得され、すぐに獲得されたとき、驚きはさらに大きくなりました。 何が、どのように、そしてなぜ時間がなかったのかを理解してください。そのため、コードはその形式のままでした。 これは低レベルの開発の欠点の1つです。コードの一部が「魔法のように」機能するという事実にしばしば同意する必要があります。 Bluetoothキーボードは、プロトタイプを1つしか使用していないため、この魔法はすぐに解消されました。



プロトタイプ#2-USB HIDキーボード



既製のデバイスの使用が私たちを大きく制限していることが明らかになったらすぐに、独自のデバイスを開発することが決定されました。 今回私たちが注目した主なスマートフォンは、Samsung Galaxy S2でした。 彼には通常の安定したUSBホストがあり、これに接続しました。 Teensy 1.0がコントローラーとして採用されました。 機会がほとんどなく、より新しいバージョンの2.0を採用する必要があることが非常に間違っていることが判明しました。 違いは、HID互換デバイスを作成するためのライブラリにありました。 1.0がHIDキーボードまたはマウスのふりしかできなかった場合、2.0では、独自のプロトコルを使用して、はるかに汎用的なRaw HIDデバイスを実装できました。 しかし、何をすべきか、最初に何に満足する必要がありました。 HIDキーボードをエミュレートするとき、上記の問題が再び発生しました-接続すると、システム自体が入力デバイスとしてキャプチャし、送信された文字が2回印刷されました-私たちとOSによって。



上記のソリューションはもはや適切ではありませんでした-彼らは私たちが十分なシステム変更があると判断しました。 市場にリリースできるデバイスを作成する場合は、Galaxy S2で動作するはずです。フラッシュする必要はありません。



S2に接続された通常のUSBキーボードで実験する過程で、何らかの理由でスマートフォンがキーボードのデジタル部分のボタン押下に応答しないことが判明しました。 これは、Teensyが数字ボタンを押す際のOctodonジョイスティックの偏差を何らかの方法でエンコードできるため、深刻な助けになりました。 ジョイスティックの状態が変化するたびに、3バイトのパッケージを送信し始めました。



  1. スキャンコード「Num 0」...「Num 9」は、ステータスを送信しているジョイスティックの番号です。
  2. スキャンコード「Num 0」...「Num 4」-ジョイスティックの逸脱の方向。 0-左に、1-下に、...、4-中央に押します。
  3. プレスがあった場合は「Num +」、リリースされた場合は「Num-」。




したがって、最初のジョイスティックを左に押すだけでシーケンス「00+」が得られ、「00-」がリリースされました。 1つのジョイスティックを離さずに、2番目のジョイスティックを拒否できます。この方法では、複数のボタンを同時に押すことをエンコードできます。これは、Shiftモードで本当に必要でした。



それでも、送信されたスキャンコードをどうにかして取得するタスクがまだありました。 システムAPIはそれらを無視したので、さらに深くしなければなりませんでした。 幸いなことに、Androidは* nixであり、それがファイルです。 目的のファイルは/ dev / inputにあり、空の星の位置に応じて、event7またはevent8と呼ばれていました。 rootのみがアクセスできるため、デバイスはrootでなければなりませんでしたが、それだけで制限されていました-フラッシュは必要ありませんでした。 Androidでrootを使用することは興味深いタスクです。APIを介して適切な権限を取得する方法がない(または見つからない)ためです。 したがって、ターミナルから直接すべてを実行します。



 Process root = Runtime.getRuntime().exec("su"); //   su DataOutputStream shellStream = new DataOutputStream(root.getOutputStream()); shellStream.writeBytes("cat /dev/input/event7 & && cat /dev/input/event8 &"); //     ,    ,    InputStream inputStream = root.getInputStream(); InputStream errorStream = root.getErrorStream(); //  16-    byte[] buf = new byte[16]; if (inputStream.available() > 15) { int data = inputStream.read(buf, 0, 16); }
      
      







このように機能したプロトタイプは、長い間存在し、多くの展示会を通過し、まだ​​生きています。 時間が経つにつれて、コードは高貴になり、ハードコードの代わりにデバイスファイル名が構成ファイルを介して指定され始めました。



さて、それから黄金時代が来ました。 Galaxy S2向けに4番目のAndroidが登場し、Teensy 2.0が注文されました。 新しいOSに登場するUSB​​ホストAPIを備えた新しいRaw HIDコントローラーに基づいて開発されたデバイスのドッキングは非常にスムーズに行われたため、問題について説明するように設計されたこのエッセイのフレームワークには収まりません。 古い夢が実現しました。Octodonは何もせずに「ストック」スマートフォンで作業することができました。 うまく機能する小規模なサンプルを作成する機会がありましたが、ファイルで頭に浮かぶ必要はありません。 しかし、松葉杖のない美しいソリューションは別の記事のトピックです。特にHabrのページでは、USBでUSB Host APIを操作することについては決して話していないようです。 続けて!



All Articles